﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace RayDen.Library.Core.Primitives
{


    [Serializable]
    [StructLayout(LayoutKind.Sequential)]
    public unsafe struct PSSpectrum : IRgbConvertible, ISerializableValue, IStringSerializable, ISpectrum
    {
        public const int MaxSpectrumSamples = 100;
        public fixed float spectra[nSpectralSamples];
        public const float Epsilon = 0.00001f;
        public const int nSpectralSamples = 80;
        public static int nSamples = 80;


        public const int sampledLambdaStart = 360;
        public const int sampledLambdaEnd = 830;
        public const int nCIESamples = sampledLambdaEnd-sampledLambdaStart;
        public static int nRGB2SpectSamples = 32;
        public readonly static PSSpectrum X, Y, Z;
        public static float yint;
        public static PSSpectrum rgbRefl2SpectWhite = new PSSpectrum(0f), rgbRefl2SpectCyan = new PSSpectrum(0f);
        public static PSSpectrum rgbRefl2SpectMagenta = new PSSpectrum(0f), rgbRefl2SpectYellow = new PSSpectrum(0f);
        public static PSSpectrum rgbRefl2SpectRed = new PSSpectrum(0f), rgbRefl2SpectGreen = new PSSpectrum(0f);
        public static PSSpectrum rgbRefl2SpectBlue = new PSSpectrum(0f);
        public static PSSpectrum rgbIllum2SpectWhite = new PSSpectrum(0f), rgbIllum2SpectCyan = new PSSpectrum(0f);
        public static PSSpectrum rgbIllum2SpectMagenta = new PSSpectrum(0f), rgbIllum2SpectYellow = new PSSpectrum(0f);
        public static PSSpectrum rgbIllum2SpectRed = new PSSpectrum(0f), rgbIllum2SpectGreen = new PSSpectrum(0f);
        public static PSSpectrum rgbIllum2SpectBlue = new PSSpectrum(0f);


        public static RgbSpectrum[] IllumRainbow
        {
            get
            {
                return new[]
                {
                    rgbIllum2SpectRed,
                    rgbIllum2SpectYellow,
                    rgbIllum2SpectGreen,
                    rgbIllum2SpectCyan,
                    rgbIllum2SpectBlue,
                    rgbIllum2SpectMagenta,
                    rgbIllum2SpectWhite,
                }.Select(item => item.ToRgb()).ToArray();
            }
        }
        public static RgbSpectrum[] ReflRainbow
        {
            get
            {
                return new[]
                {
                    rgbRefl2SpectWhite,
                    rgbRefl2SpectMagenta,
                    rgbRefl2SpectRed,
                    rgbRefl2SpectBlue,
                    rgbRefl2SpectCyan,
                    rgbRefl2SpectYellow,
                    rgbRefl2SpectGreen
                }.Select(item => item.ToRgb()).ToArray();
            }
        }
        public PSSpectrum(float a = 0f)
        {
            //spectra = new float[nSamples];
            if (a.NearEqual(MathLab.SpectreEpsilon))
                return;
            {
                fixed (float* sp = spectra)
                    for (int i = 0; i < nSamples; i++)
                        sp[i] = a;
            }
        }

        public PSSpectrum(ref RgbSpectrum c, SpectrumType type)
        {
            //fixed spectra = new float*[nSamples];
            FromRgb(ref c, type);
        }

        public PSSpectrum(RgbSpectrum c, SpectrumType type)
        {
            //spectra = new float[nSamples];
            FromRgb(ref c, type);
        }


        public PSSpectrum(float[] d)
        {
            if (d.Length == nSpectralSamples)
            {
                fixed (float* c = spectra)
                {
                    MemCopy(d, c);
                }
                //spectra = new float[d.Length];
                //float[] arr = new float[nSpectralSamples];
                //for (int i = 0; i < nSpectralSamples; i++)
                //  this.spectra[i] =d[i];
                //Array.Copy(d, spectra, d.Length);
            }
            else
            {
                this = FromRGB(d, SpectrumType.Illuminant);
            }
        }

        public PSSpectrum(PSSpectrum sp)
        {
            //spectra = new float[nSamples];
            fixed (float* spd = spectra)
                for (int i = 0; i < nSamples; i++)
                    spd[i] = sp.spectra[i];
        }

        public static void XYZToRGB(float[] xyz, out float[] rgb)
        {
            rgb = new float[3];
            rgb[0] = 3.240479f * xyz[0] - 1.537150f * xyz[1] - 0.498535f * xyz[2];
            rgb[1] = -0.969256f * xyz[0] + 1.875991f * xyz[1] + 0.041556f * xyz[2];
            rgb[2] = 0.055648f * xyz[0] - 0.204043f * xyz[1] + 1.057311f * xyz[2];
        }


        public static void RGBToXYZ(float[] rgb, out float[] xyz)
        {
            xyz = new float[3];
            xyz[0] = 0.412453f * rgb[0] + 0.357580f * rgb[1] + 0.180423f * rgb[2];
            xyz[1] = 0.212671f * rgb[0] + 0.715160f * rgb[1] + 0.072169f * rgb[2];
            xyz[2] = 0.019334f * rgb[0] + 0.119193f * rgb[1] + 0.950227f * rgb[2];
        }

        #region Operators

        public static PSSpectrum operator +(PSSpectrum a, PSSpectrum b)
        {
            var result = new PSSpectrum(0f);
            for (int i = 0; i < nSamples; i++)
            {
                result.spectra[i] = a.spectra[i] + b.spectra[i];
            }
            return result;
        }


        public static PSSpectrum operator *(PSSpectrum a, PSSpectrum b)
        {
            var result = new PSSpectrum(0f);
            for (int i = 0; i < nSamples; i++)
            {
                result.spectra[i] = a.spectra[i] * b.spectra[i];
            }
            return result;
        }

        public static PSSpectrum operator /(PSSpectrum a, PSSpectrum b)
        {
            var result = new PSSpectrum(0f);
            for (int i = 0; i < nSamples; i++)
            {
                result.spectra[i] = a.spectra[i] / b.spectra[i];
            }
            return result;
        }

        public static PSSpectrum operator -(PSSpectrum a, PSSpectrum b)
        {
            var result = new PSSpectrum(0f);
            for (int i = 0; i < nSamples; i++)
            {
                result.spectra[i] = a.spectra[i] - b.spectra[i];
            }
            return result;
        }


        public static PSSpectrum operator *(PSSpectrum a, float b)
        {
            var result = new PSSpectrum(0f);
            for (int i = 0; i < nSamples; i++)
            {
                result.spectra[i] = a.spectra[i] * b;
            }
            return result;
        }


        public static PSSpectrum operator +(PSSpectrum a, float b)
        {
            var result = new PSSpectrum(0f);
            for (int i = 0; i < nSamples; i++)
            {
                result.spectra[i] = a.spectra[i] + b;
            }
            return result;
        }

        public static PSSpectrum operator -(PSSpectrum a, float b)
        {
            var result = new PSSpectrum(0f);
            for (int i = 0; i < nSamples; i++)
            {
                result.spectra[i] = a.spectra[i] - b;
            }
            return result;
        }
        public static PSSpectrum operator *(float b, PSSpectrum a)
        {
            var result = new PSSpectrum(0f);
            for (int i = 0; i < nSamples; i++)
            {
                result.spectra[i] = a.spectra[i] * b;
            }
            return result;
        }

        public static PSSpectrum operator /(PSSpectrum a, float b)
        {
            var result = new PSSpectrum(0f);
            for (int i = 0; i < nSamples; i++)
            {
                result.spectra[i] = a.spectra[i] / b;
            }
            return result;
        }

        #endregion

        #region Properties

        const float OneThird = 1f / 3f;
        public float Average
        {
            get
            {
                float avg = 0f;
                for (int i = 0; i < nSpectralSamples; i++)
                {
                    avg += this[i];
                }
                return avg / nSpectralSamples;
            }
        }



        public float y()
        {
            float v = 0f;
            for (int i = 0; i < nSpectralSamples; i++)
                v += YWeight[i] * this[i];
            return v / yint;
        }
        #endregion


        public PSSpectrum Sqrt()
        {
            fixed (float* spd = spectra)
                for (int i = 0; i < nSamples; i++)
                {
                    spd[i] = MathLab.Sqrt(spd[i]);
                }

            return this;
        }

        public void Clip(float min, float max)
        {
            for (int i = 0; i < nSamples; i++)
            {
                if (this[i] < min || float.IsNaN(this[i]))
                    this[i] = min;
                else if (this[i] > max)
                    this[i] = max;
            }

        }

        public void Div(float b)
        {
            this = this / b;
        }

        public void Mul(ref ISpectrum s)
        {
            if (s is RgbSpectrum)
            {
                this *= FromRGB(((RgbSpectrum) s).ToArray(), SpectrumType.Illuminant);
            }
            else
            {
                var a = (PSSpectrum) s;
                this *= a;
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void MAdd(ref PSSpectrum sp, float f)
        {
            for (int i = 0; i < nSamples; i++)
            {
                this[i] += sp[i] * f;
            }
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void MAdd(ref PSSpectrum sp, ref PSSpectrum f)
        {
            for (int i = 0; i < nSamples; i++)
            {
                this[i] += sp[i] * f[i];
            }
        }
        public Color Transform()
        {
            var rgb = new float[3];
            this.ToRGB(out rgb);
            return new RgbSpectrum(rgb[0], rgb[1], rgb[2]).ToColor();
        }

        public RgbSpectrum ToRgb()
        {
            //const float rangeScale = 255.0f;
            var rgb = new float[3];
            this.ToRGB(out rgb);
            return new RgbSpectrum(rgb);
        }

        public void AssertNaN()
        {
            for (int i = 0; i < nSamples; i++)
            {
                if (float.IsNaN(this[i]))
                {
                    Debugger.Break();
                }
            }
        }

        public static bool SpectrumSamplesSorted(float[] lambda, float[] vals, int n)
        {
            for (int i = 0; i < n - 1; i++)
                if (lambda[i] > lambda[i + 1])
                    return false;
            return true;
        }

        public static void SortSpectrumSamples(ref float[] lambda, ref float[] vals)
        {
            lambda = lambda.OrderBy(i => i).ToArray();
            vals = vals.OrderBy(i => i).ToArray();
        }

        public static float AverageSpectrumSamples(float[] lambda, float[] vals, int n, float lambdaStart, float lambdaEnd)
        {
            for (int k = 0; k < n - 1; k++) if (!(lambda[k + 1] > lambda[k]))
                {
                    SortSpectrumSamples(ref lambda, ref vals);
                    //throw new ArgumentException("Spectral data not sorted");
                }
            Assert(lambdaStart < lambdaEnd);
            if (!SpectrumSamplesSorted(lambda, vals, n))
                SortSpectrumSamples(ref lambda, ref vals);
            // Handle cases with out-of-bounds range or single sample only
            if (lambdaEnd <= lambda[0])
                return vals[0];
            if (lambdaStart >= lambda[n - 1])
                return vals[n - 1];
            if (n == 1)
                return vals[0];
            float sum = 0f;
            // Add contributions of constant segments before/after samples
            if (lambdaStart < lambda[0])
                sum += vals[0] * (lambda[0] - lambdaStart);
            if (lambdaEnd > lambda[n - 1])
                sum += vals[n - 1] * (lambdaEnd - lambda[n - 1]);

            // Advance to first relevant wavelength segment
            int i = 0;
            while (lambdaStart > lambda[i + 1])
                ++i;
            Assert(i + 1 < n);

            // Loop over wavelength sample segments and add contributions

            float sm = 0f;
            for (; i + 1 < n && lambdaEnd >= lambda[i]; ++i)
            {
                float segStart = Math.Max(lambdaStart, lambda[i]);
                float segEnd = Math.Min(lambdaEnd, lambda[i + 1]);
                sum += (segEnd - segStart) * SEG(segStart, segEnd, i, lambda, vals);
                sm += (segEnd - segStart) *
                      (0.5f * (MathLab.Lerp(((segStart) - lambda[i]) / (lambda[(i) + 1] - lambda[i]), vals[i], vals[(i) + 1])
                               +
                               (MathLab.Lerp(((segEnd) - lambda[i]) / (lambda[(i) + 1] - lambda[i]), vals[i], vals[(i) + 1])))
                      );
            }

            return sum / (lambdaEnd - lambdaStart);
        }


        private static float LERP(float w, float li, float li1, float vi, float vi1)
        {
            return MathLab.Lerp((w - li) / (li1 - li), vi, vi1);
        }

        private static float SEG(float wl0, float wl1, int i, float[] lambda, float[] val)
        {
            return 0.5f *
                   (LERP(wl0, lambda[i], lambda[i + 1], val[i], val[i + 1]) + LERP(wl1, lambda[i], lambda[i + 1], val[i], val[i + 1]));
        }

        private static void Assert(bool p, string message = null)
        {
            if (!p)
                throw new ArgumentException(message ?? "Assertion");
        }

        static PSSpectrum()
        {
            X = new PSSpectrum(0f);
            Y = new PSSpectrum(0f);
            Z = new PSSpectrum(0f);
            // Compute XYZ matching functions for _PSSpectrum_
            for (int i = 0; i < nSpectralSamples; i++)
            {
                float wl0 = MathLab.Lerp(i / (float)nSpectralSamples, sampledLambdaStart, sampledLambdaEnd);
                float wl1 = MathLab.Lerp((i + 1f) / nSpectralSamples, sampledLambdaStart, sampledLambdaEnd);
                X[i] = AverageSpectrumSamples(CIE_lambda, CIE_X, nCIESamples, wl0, wl1);
                Y[i] = AverageSpectrumSamples(CIE_lambda, CIE_Y, nCIESamples, wl0, wl1);
                Z[i] = AverageSpectrumSamples(CIE_lambda, CIE_Z, nCIESamples, wl0, wl1);
                yint += Y[i];
            }
            Init();
        }

        public static void Init()
        {
            try
            {
                // Compute RGB to spectrum functions for _PSSpectrum_
                for (int i = 0; i < nSpectralSamples; i++)
                {
                    float wl0 = MathLab.Lerp(i / (float)nSpectralSamples, sampledLambdaStart, sampledLambdaEnd);
                    float wl1 = MathLab.Lerp((i + 1f) / nSpectralSamples, sampledLambdaStart, sampledLambdaEnd);
                    rgbRefl2SpectWhite[i] = AverageSpectrumSamples(RGB2SpectLambda, RGBRefl2SpectWhite, nRGB2SpectSamples, wl0, wl1);
                    rgbRefl2SpectCyan[i] = AverageSpectrumSamples(RGB2SpectLambda, RGBRefl2SpectCyan, nRGB2SpectSamples, wl0, wl1);
                    rgbRefl2SpectMagenta[i] = AverageSpectrumSamples(RGB2SpectLambda, RGBRefl2SpectMagenta, nRGB2SpectSamples, wl0, wl1);
                    rgbRefl2SpectYellow[i] = AverageSpectrumSamples(RGB2SpectLambda, RGBRefl2SpectYellow, nRGB2SpectSamples, wl0, wl1);
                    rgbRefl2SpectRed[i] = AverageSpectrumSamples(RGB2SpectLambda, RGBRefl2SpectRed, nRGB2SpectSamples, wl0, wl1);
                    rgbRefl2SpectGreen[i] = AverageSpectrumSamples(RGB2SpectLambda, RGBRefl2SpectGreen, nRGB2SpectSamples, wl0, wl1);
                    rgbRefl2SpectBlue[i] = AverageSpectrumSamples(RGB2SpectLambda, RGBRefl2SpectBlue, nRGB2SpectSamples, wl0, wl1);

                    //Console.WriteLine("С# {0} nth Color Spectra, Yellow = {1:F4}, Red = {2:F4} , Blue = {3:F4} ", i, rgbRefl2SpectYellow.spectra[i], rgbRefl2SpectRed.spectra[i], rgbRefl2SpectBlue.spectra[i]);

                    rgbIllum2SpectWhite[i] = AverageSpectrumSamples(RGB2SpectLambda, RGBIllum2SpectWhite, nRGB2SpectSamples, wl0, wl1);
                    rgbIllum2SpectCyan[i] = AverageSpectrumSamples(RGB2SpectLambda, RGBIllum2SpectCyan, nRGB2SpectSamples, wl0, wl1);
                    rgbIllum2SpectMagenta[i] = AverageSpectrumSamples(RGB2SpectLambda, RGBIllum2SpectMagenta, nRGB2SpectSamples, wl0, wl1);
                    rgbIllum2SpectYellow[i] = AverageSpectrumSamples(RGB2SpectLambda, RGBIllum2SpectYellow, nRGB2SpectSamples, wl0, wl1);
                    rgbIllum2SpectRed[i] = AverageSpectrumSamples(RGB2SpectLambda, RGBIllum2SpectRed, nRGB2SpectSamples, wl0, wl1);
                    rgbIllum2SpectGreen[i] = AverageSpectrumSamples(RGB2SpectLambda, RGBIllum2SpectGreen, nRGB2SpectSamples, wl0, wl1);
                    rgbIllum2SpectBlue[i] = AverageSpectrumSamples(RGB2SpectLambda, RGBIllum2SpectBlue, nRGB2SpectSamples, wl0, wl1);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        public void ToRGB(out float[] rgb)
        {
            var xyz = new float[3];
            this.XYZ(ref xyz);
            XYZToRGB(xyz, out rgb);
        }

        public void FromRgb(ref RgbSpectrum rgbSp, SpectrumType type = SpectrumType.Reflectance)
        {
            var rgb = rgbSp.ToArray();
            var c = FromRGB(rgb, type);
            for (int i = 0; i < nSamples; i++)
            {
                this[i] = c[i];
            }
        }

        //public static PSSpectrum FromRgb(ref RgbSpectrum rgbSp, SpectrumType type = SpectrumType.Reflectance)
        //{
        //    var rgb = rgbSp.ToArray();

        //    return FromRGB(rgb, type);
        //}

        public static PSSpectrum FromRGB_old(float[] rgb, SpectrumType type = SpectrumType.Reflectance)
        {
            var r = new PSSpectrum(0f);
            if (type == SpectrumType.Reflectance)
            {
                // Convert reflectance spectrum to RGB
                if ((rgb[0] <= rgb[1]) && (rgb[0] <= rgb[2]))
                {
                    // Compute reflectance _PSSpectrum_ with _rgb[0]_ as minimum
                    r += rgb[0] * rgbRefl2SpectWhite;
                    if (rgb[1] <= rgb[2])
                    {
                        r += (rgb[1] - rgb[0]) * rgbRefl2SpectCyan;
                        r += (rgb[2] - rgb[1]) * rgbRefl2SpectBlue;
                    }
                    else
                    {
                        r += (rgb[2] - rgb[0]) * rgbRefl2SpectCyan;
                        r += (rgb[1] - rgb[2]) * rgbRefl2SpectGreen;
                    }
                }
                else if (rgb[1] <= rgb[0] && rgb[1] <= rgb[2])
                {
                    // Compute reflectance _PSSpectrum_ with _rgb[1]_ as minimum
                    r += rgb[1] * rgbRefl2SpectWhite;
                    if (rgb[0] <= rgb[2])
                    {
                        r += (rgb[0] - rgb[1]) * rgbRefl2SpectMagenta;
                        r += (rgb[2] - rgb[0]) * rgbRefl2SpectBlue;
                    }
                    else
                    {
                        r += (rgb[2] - rgb[1]) * rgbRefl2SpectMagenta;
                        r += (rgb[0] - rgb[2]) * rgbRefl2SpectRed;
                    }
                }
                else
                {
                    // Compute reflectance _PSSpectrum_ with _rgb[2]_ as minimum
                    r += rgb[2] * rgbRefl2SpectWhite;
                    if (rgb[0] <= rgb[1])
                    {
                        r += (rgb[0] - rgb[2]) * rgbRefl2SpectYellow;
                        r += (rgb[1] - rgb[0]) * rgbRefl2SpectGreen;
                    }
                    else
                    {
                        r += (rgb[1] - rgb[2]) * rgbRefl2SpectYellow;
                        r += (rgb[0] - rgb[1]) * rgbRefl2SpectRed;
                    }
                }
                r *= .94f;
            }
            else
            {
                // Convert illuminant spectrum to RGB
                if (rgb[0] <= rgb[1] && rgb[0] <= rgb[2])
                {
                    // Compute illuminant _PSSpectrum_ with _rgb[0]_ as minimum
                    r += rgb[0] * rgbIllum2SpectWhite;
                    if (rgb[1] <= rgb[2])
                    {
                        r += (rgb[1] - rgb[0]) * rgbIllum2SpectCyan;
                        r += (rgb[2] - rgb[1]) * rgbIllum2SpectBlue;
                    }
                    else
                    {
                        r += (rgb[2] - rgb[0]) * rgbIllum2SpectCyan;
                        r += (rgb[1] - rgb[2]) * rgbIllum2SpectGreen;
                    }
                }
                else if (rgb[1] <= rgb[0] && rgb[1] <= rgb[2])
                {
                    // Compute illuminant _PSSpectrum_ with _rgb[1]_ as minimum
                    r += rgb[1] * rgbIllum2SpectWhite;
                    if (rgb[0] <= rgb[2])
                    {
                        r += (rgb[0] - rgb[1]) * rgbIllum2SpectMagenta;
                        r += (rgb[2] - rgb[0]) * rgbIllum2SpectBlue;
                    }
                    else
                    {
                        r += (rgb[2] - rgb[1]) * rgbIllum2SpectMagenta;
                        r += (rgb[0] - rgb[2]) * rgbIllum2SpectRed;
                    }
                }
                else
                {
                    // Compute illuminant _PSSpectrum_ with _rgb[2]_ as minimum
                    r += rgb[2] * rgbIllum2SpectWhite;
                    if (rgb[0] <= rgb[1])
                    {
                        r += (rgb[0] - rgb[2]) * rgbIllum2SpectYellow;
                        r += (rgb[1] - rgb[0]) * rgbIllum2SpectGreen;
                    }
                    else
                    {
                        r += (rgb[1] - rgb[2]) * rgbIllum2SpectYellow;
                        r += (rgb[0] - rgb[1]) * rgbIllum2SpectRed;
                    }
                }
                r *= .86445f;
            }
            return r.Clamp();
        }

        public static PSSpectrum FromRGB(float[] rgb, SpectrumType type = SpectrumType.Reflectance)
        {
            var r = new PSSpectrum(0f);
            if (type == SpectrumType.Reflectance)
            {
                // Convert reflectance spectrum to RGB
                if (rgb[0] <= rgb[1] && rgb[0] <= rgb[2])
                {
                    // Compute reflectance _PSSpectrum_ with _rgb[0]_ as minimum
                    //r += rgb[0] * rgbRefl2SpectWhite;
                    r.MAdd(ref rgbRefl2SpectWhite, rgb[0]);
                    if (rgb[1] <= rgb[2])
                    {
                        r.MAdd(ref rgbRefl2SpectCyan, (rgb[1] - rgb[0]));
                        r.MAdd(ref rgbRefl2SpectBlue, (rgb[2] - rgb[1]));
                        //r += (rgb[1] - rgb[0]) * rgbRefl2SpectCyan;
                        //r += (rgb[2] - rgb[1]) * rgbRefl2SpectBlue;
                    }
                    else
                    {
                        //r += (rgb[2] - rgb[0]) * rgbRefl2SpectCyan;
                        //r += (rgb[1] - rgb[2]) * rgbRefl2SpectGreen;

                        r.MAdd(ref rgbRefl2SpectCyan, (rgb[2] - rgb[0]));
                        r.MAdd(ref rgbRefl2SpectGreen, (rgb[1] - rgb[2]));
                    }
                }
                else if (rgb[1] <= rgb[0] && rgb[1] <= rgb[2])
                {
                    // Compute reflectance _PSSpectrum_ with _rgb[1]_ as minimum
                    //r += rgb[1] * rgbRefl2SpectWhite;
                    r.MAdd(ref rgbRefl2SpectWhite, rgb[1]);

                    if (rgb[0] <= rgb[2])
                    {
                        //r += (rgb[0] - rgb[1]) * rgbRefl2SpectMagenta;
                        //r += (rgb[2] - rgb[0]) * rgbRefl2SpectBlue;

                        r.MAdd(ref rgbRefl2SpectMagenta, (rgb[0] - rgb[1]));
                        r.MAdd(ref rgbRefl2SpectBlue, (rgb[2] - rgb[0]));
                    }
                    else
                    {
                        //r += (rgb[2] - rgb[1]) * rgbRefl2SpectMagenta;
                        //r += (rgb[0] - rgb[2]) * rgbRefl2SpectRed;
                        r.MAdd(ref rgbRefl2SpectMagenta, (rgb[2] - rgb[1]));
                        r.MAdd(ref rgbRefl2SpectRed, (rgb[0] - rgb[2]));
                    }
                }
                else
                {
                    // Compute reflectance _PSSpectrum_ with _rgb[2]_ as minimum
                    //r += rgb[2] * rgbRefl2SpectWhite;
                    r.MAdd(ref rgbRefl2SpectWhite, rgb[2]);

                    if (rgb[0] <= rgb[1])
                    {
                        //r += (rgb[0] - rgb[2]) * rgbRefl2SpectYellow;
                        //r += (rgb[1] - rgb[0]) * rgbRefl2SpectGreen;
                        r.MAdd(ref rgbRefl2SpectYellow, (rgb[0] - rgb[2]));
                        r.MAdd(ref rgbRefl2SpectGreen, (rgb[1] - rgb[0]));
                    }
                    else
                    {
                        //r += (rgb[1] - rgb[2]) * rgbRefl2SpectYellow;
                        //r += (rgb[0] - rgb[1]) * rgbRefl2SpectRed;

                        r.MAdd(ref rgbRefl2SpectYellow, (rgb[1] - rgb[2]));
                        r.MAdd(ref rgbRefl2SpectRed, (rgb[0] - rgb[1]));
                    }
                }
                r *= .94f;
            }
            else
            {
                // Convert illuminant spectrum to RGB
                if (rgb[0] <= rgb[1] && rgb[0] <= rgb[2])
                {
                    // Compute illuminant _PSSpectrum_ with _rgb[0]_ as minimum
                    //r += rgb[0] * rgbIllum2SpectWhite;
                    r.MAdd(ref rgbIllum2SpectWhite, rgb[0]);
                    if (rgb[1] <= rgb[2])
                    {
                        //r += (rgb[1] - rgb[0]) * rgbIllum2SpectCyan;
                        //r += (rgb[2] - rgb[1]) * rgbIllum2SpectBlue;
                        r.MAdd(ref rgbIllum2SpectCyan, (rgb[1] - rgb[0]));
                        r.MAdd(ref rgbIllum2SpectBlue, (rgb[2] - rgb[1]));

                    }
                    else
                    {
                        // r += (rgb[2] - rgb[0]) * rgbIllum2SpectCyan;
                        //r += (rgb[1] - rgb[2]) * rgbIllum2SpectGreen;

                        r.MAdd(ref rgbIllum2SpectCyan, (rgb[2] - rgb[0]));
                        r.MAdd(ref rgbIllum2SpectGreen, (rgb[1] - rgb[2]));
                    }
                }
                else if (rgb[1] <= rgb[0] && rgb[1] <= rgb[2])
                {
                    // Compute illuminant _PSSpectrum_ with _rgb[1]_ as minimum
                    //r += rgb[1] * rgbIllum2SpectWhite;
                    r.MAdd(ref rgbIllum2SpectWhite, rgb[1]);

                    if (rgb[0] <= rgb[2])
                    {
                        //r += (rgb[0] - rgb[1]) * rgbIllum2SpectMagenta;
                        //r += (rgb[2] - rgb[0]) * rgbIllum2SpectBlue;

                        r.MAdd(ref rgbIllum2SpectMagenta, (rgb[0] - rgb[1]));
                        r.MAdd(ref rgbIllum2SpectBlue, (rgb[2] - rgb[0]));
                    }
                    else
                    {
                        //r += (rgb[2] - rgb[1]) * rgbIllum2SpectMagenta;
                        //r += (rgb[0] - rgb[2]) * rgbIllum2SpectRed;

                        r.MAdd(ref rgbIllum2SpectMagenta, (rgb[2] - rgb[1]));
                        r.MAdd(ref rgbIllum2SpectRed, (rgb[0] - rgb[2]));
                    }
                }
                else
                {
                    // Compute illuminant _PSSpectrum_ with _rgb[2]_ as minimum
                    //r += rgb[2] * rgbIllum2SpectWhite;
                    r.MAdd(ref rgbIllum2SpectWhite, rgb[2]);

                    if (rgb[0] <= rgb[1])
                    {
                        //r += (rgb[0] - rgb[2]) * rgbIllum2SpectYellow;
                        //r += (rgb[1] - rgb[0]) * rgbIllum2SpectGreen;

                        r.MAdd(ref rgbIllum2SpectYellow, (rgb[0] - rgb[2]));
                        r.MAdd(ref rgbIllum2SpectGreen, (rgb[1] - rgb[0]));
                    }
                    else
                    {
                        //r += (rgb[1] - rgb[2]) * rgbIllum2SpectYellow;
                        //r += (rgb[0] - rgb[1]) * rgbIllum2SpectRed;

                        r.MAdd(ref rgbIllum2SpectYellow, (rgb[1] - rgb[2]));
                        r.MAdd(ref rgbIllum2SpectRed, (rgb[0] - rgb[1]));
                    }
                }
                r *= .86445f;
            }
            r.ClampSelf();
            return r;
        }

        public static PSSpectrum FromSampled(float[] lambda, float[] values, SpectrumType type = SpectrumType.Reflectance)
        {
            var n = lambda.Length;
            if (!SpectrumSamplesSorted(lambda, values, lambda.Length))
            {
                SortSpectrumSamples(ref lambda, ref values);
                return FromSampled(lambda, values, type);
            }
            var r = new PSSpectrum(0f);
            for (int i = 0; i < nSpectralSamples; ++i)
            {
                // Compute average value of given SPD over $i$th sample's range
                float lambda0 = MathLab.Lerp((float)(i) / (float)(nSpectralSamples), sampledLambdaStart, sampledLambdaEnd);
                float lambda1 = MathLab.Lerp((float)(i + 1f) / (float)(nSpectralSamples), sampledLambdaStart, sampledLambdaEnd);
                r.spectra[i] = AverageSpectrumSamples(lambda, values, n, lambda0, lambda1);
            }
            return r;
        }



        public static PSSpectrum Lerp(float t, ref PSSpectrum s1, ref PSSpectrum s2)
        {
            return (1f - t) * s1 + t * s2;
        }

        public static void Blackbody(float[] wl, int n, float temp, float[] vals)
        {
            if (temp <= 0)
            {
                for (int i = 0; i < n; ++i) vals[i] = 0f;
                return;
            }
            const double C2 = 1.4388e7;
            double norm = Math.Pow(555.0, 5.0) * (Math.Exp(C2 / (555.0 * temp)) - 1d);
            for (int i = 0; i < n; ++i)
                vals[i] = (float)(norm / (Math.Pow((double)(wl[i]), 5.0) * (Math.Exp(C2 / (wl[i] * temp)) - 1d)));
        }

        public static PSSpectrum FromXYZ(float[] xyz, SpectrumType type = SpectrumType.Reflectance)
        {
            float[] rgb = null;
            XYZToRGB(xyz, out rgb);
            return FromRGB(rgb, type);
        }

        public static PSSpectrum Create(Color clrColor)
        {
            return FromRGB(new[] { (float)clrColor.R / 255.0f, (float)clrColor.G / 255.0f, (float)clrColor.B / 255.0f });
        }

        public static PSSpectrum Create(byte clrColorR, byte clrColorG, byte clrColorB)
        {
            return FromRGB(new[] { (float)clrColorR / 255.0f, (float)clrColorG / 255.0f, (float)clrColorB / 255.0f });
        }

        public static PSSpectrum UnitSpectrum()
        {
            return new PSSpectrum(1f);
        }

        /*
        unsafe public static PSSpectrum Create(int* pixels, int width, int height, float u, float v) {
            return Create(Color.FromArgb(pixels[((int)(v * (height - 1))) * width + ((int)(u * (width - 1)))]));
        }
        */
        public static PSSpectrum Create(Bitmap bitmap, float u, float v)
        {
            return Create(bitmap.GetPixel((int)(u * (bitmap.Width - 1)),
                                          (int)(v * (bitmap.Height - 1))));
        }



        public bool IsBlack()
        {
            return this.Average < Epsilon;
        }


        public static PSSpectrum Black
        {
            get
            {
                return new PSSpectrum(Epsilon);
            }
        }

        public override string ToString()
        {
            string format = string.Empty;
            var data = this.ToArray();
            for (int i = 0; i < nSpectralSamples; i++)
            {
                format += string.Format("{0:F5},", data[i]);
            }
            return string.Format(format, this.ToArray()).Trim(',');
        }

        public float[] ToArray()
        {
            float[] arr = new float[nSpectralSamples];
            fixed (float* spd = this.spectra)
                for (int i = 0; i < nSpectralSamples; i++)
                    arr[i] = spd[i];
            return arr;
        }

        public void ToSpectra(out float[] lambda, out float[] values)
        {
            int dx = (sampledLambdaEnd - sampledLambdaStart)/nSamples;
            lambda = new float[nSamples];
            values = new float[nSamples];
            for (int i = sampledLambdaStart, j = 0; j < nSamples; i+=dx,j++)
            {
                lambda[j] = i;
                values[j] = this[j];
            }
        }

        public static PSSpectrum ZeroSpectrum()
        {
            return new PSSpectrum(0f);
        }

        internal bool IsZero()
        {
            return this.ToArray().All(p => p < Epsilon);
        }


        public PSSpectrum Pow(RgbSpectrum e)
        {
            PSSpectrum ret = new PSSpectrum(0f);
            for (int i = 0; i < nSamples; i++)
                ret[i] = this[i] > 0 ? (float)Math.Pow(this[i], (float)e[i]) : 0f;
            return ret;
        }

        public static PSSpectrum Exp(RgbSpectrum s)
        {
            PSSpectrum ret = new PSSpectrum(0f);
            for (int i = 0; i < nSamples; i++)
                ret[i] = (float)Math.Exp((float)s[i]);
            return ret;
        }

        public PSSpectrum Clamp(float low = 0f, float high = float.MaxValue)
        {
            PSSpectrum ret = new PSSpectrum(0f);
            fixed (float* spd = spectra)
                for (int i = 0; i < nSamples; i++)
                    ret[i] = Clamp(spd[i], low, high);
            return ret;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ClampSelf(float low = 0f, float high = float.MaxValue)
        {
            for (int i = 0; i < nSamples; i++)
                this[i] = Clamp(this[i], low, high);
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static float Clamp(float p, float low, float high)
        {
            return Math.Min(high, Math.Max(low, p));
        }

        public void XYZ(ref float[] xyz)
        {
            xyz[0] = xyz[1] = xyz[2] = 0f;
            fixed (float* spd = spectra)
                for (int i = 0; i < nSpectralSamples; i++)
                {
                    xyz[0] += X[i] * spd[i];
                    xyz[1] += Y[i] * spd[i];
                    xyz[2] += Z[i] * spd[i];
                }

            xyz[0] /= yint;
            xyz[1] /= yint;
            xyz[2] /= yint;
        }


        public float[] XYZ()
        {
            var xyz = new float[3];
            xyz[0] = xyz[1] = xyz[2] = 0f;
            fixed (float* spd = spectra)
                for (int i = 0; i < nSpectralSamples; i++)
                {
                    xyz[0] += X[i] * spd[i];
                    xyz[1] += Y[i] * spd[i];
                    xyz[2] += Z[i] * spd[i];
                }
            xyz[0] /= yint;
            xyz[1] /= yint;
            xyz[2] /= yint;

            return xyz;
        }

        public void XYZ_Fit(ref float[] xyz)
        {
            xyz[0] = xyz[1] = xyz[2] = 0f;
            float x = 0, y = 0, z = 0;
            fixed (float* spd = spectra)
                for (int i = 0; i < nSpectralSamples; i++)
                {
                    x = (float)(0.398 * Math.Exp(-1250.0 * (Math.Log((spd[i] + 570.1) / 1014) * Math.Log((spd[i] + 570.1) / 1014)))
                                    + 1.132 * Math.Exp(-234.0 * (Math.Log((1338 - spd[i]) / 743.5) * Math.Log((1338 - spd[i]) / 743.5))));
                    if (!float.IsNaN(x))
                    {
                        xyz[0] += x;
                    }

                    y = (float)(1.011 * (Math.Exp(-0.5 * ((spd[i] - 556.1) / 46.14) * ((spd[i] - 556.1) / 46.14))));
                    if (!float.IsNaN(y))
                    {
                        xyz[1] += y;
                    }

                    z = (float)(2.060 * Math.Exp(-32 * (Math.Log((spd[i] - 265.8) / 180.4) * Math.Log((spd[i] - 265.8) / 180.4))));

                    if (!float.IsNaN(z))
                    {
                        xyz[2] += z;
                    }
                }
        }


        public static float[] XWeight = new[] { 0.412453f, 0.357580f, 0.180423f };
        public static float[] YWeight = new[] { 0.212671f, 0.715160f, 0.072169f };
        public static float[] ZWeight = new[] { 0.019334f, 0.119193f, 0.950227f };

        public static float[] rWeight = new[] { 3.240479f, -1.537150f, -0.498535f };
        public static float[] gWeight = new[] { -0.969256f, 1.875991f, 0.041556f };
        public static float[] bWeight = new[] { 0.055648f, -0.0204043f, 1.057311f };

        public float Filter()
        {

            float max = 0f;
            for (int i = 0; i < nSpectralSamples; i++)
            {
                if (this[i] > max)
                    max = this[i];
            }
            return max;
        }


        #region IComparable<RgbSpectrum> Members

        public int CompareTo(RgbSpectrum other)
        {
            return this.y().CompareTo(other.y());
        }

        #endregion


        public PSSpectrum Clone()
        {
            return new PSSpectrum(this);
        }


        #region Constant Tables

        public const int CIEstart = 360;
        public const int CIEend = 830;
        public const int nCIE = CIEend - CIEstart + 1;


        public static float[] CIE_X = new float[nCIE] {
            0.0001299000f, 0.0001458470f, 0.0001638021f, 0.0001840037f,
            0.0002066902f,  0.0002321000f,  0.0002607280f,  0.0002930750f,
            0.0003293880f,  0.0003699140f,  0.0004149000f,  0.0004641587f,
            0.0005189860f,  0.0005818540f,  0.0006552347f,  0.0007416000f,
            0.0008450296f,  0.0009645268f,  0.001094949f,  0.001231154f,
            0.001368000f,  0.001502050f,  0.001642328f,  0.001802382f,
            0.001995757f,  0.002236000f,  0.002535385f,  0.002892603f,
            0.003300829f,  0.003753236f,  0.004243000f,  0.004762389f,
            0.005330048f,  0.005978712f,  0.006741117f,  0.007650000f,
            0.008751373f,  0.01002888f,  0.01142170f,  0.01286901f,
            0.01431000f,  0.01570443f,  0.01714744f,  0.01878122f,
            0.02074801f,  0.02319000f,  0.02620736f,  0.02978248f,
            0.03388092f,  0.03846824f,  0.04351000f,  0.04899560f,
            0.05502260f,  0.06171880f,  0.06921200f,  0.07763000f,
            0.08695811f,  0.09717672f,  0.1084063f,  0.1207672f,
            0.1343800f,  0.1493582f,  0.1653957f,  0.1819831f,
            0.1986110f,  0.2147700f,  0.2301868f,  0.2448797f,
            0.2587773f,  0.2718079f,  0.2839000f,  0.2949438f,
            0.3048965f,  0.3137873f,  0.3216454f,  0.3285000f,
            0.3343513f,  0.3392101f,  0.3431213f,  0.3461296f,
            0.3482800f,  0.3495999f,  0.3501474f,  0.3500130f,
            0.3492870f,  0.3480600f,  0.3463733f,  0.3442624f,
            0.3418088f,  0.3390941f,  0.3362000f,  0.3331977f,
            0.3300411f,  0.3266357f,  0.3228868f,  0.3187000f,
            0.3140251f,  0.3088840f,  0.3032904f,  0.2972579f,
            0.2908000f,  0.2839701f,  0.2767214f,  0.2689178f,
            0.2604227f,  0.2511000f,  0.2408475f,  0.2298512f,
            0.2184072f,  0.2068115f,  0.1953600f,  0.1842136f,
            0.1733273f,  0.1626881f,  0.1522833f,  0.1421000f,
            0.1321786f,  0.1225696f,  0.1132752f,  0.1042979f,
            0.09564000f,  0.08729955f,  0.07930804f,  0.07171776f,
            0.06458099f,  0.05795001f,  0.05186211f,  0.04628152f,
            0.04115088f,  0.03641283f,  0.03201000f,  0.02791720f,
            0.02414440f,  0.02068700f,  0.01754040f,  0.01470000f,
            0.01216179f,  0.009919960f,  0.007967240f,  0.006296346f,
            0.004900000f,  0.003777173f,  0.002945320f,  0.002424880f,
            0.002236293f,  0.002400000f,  0.002925520f,  0.003836560f,
            0.005174840f,  0.006982080f,  0.009300000f,  0.01214949f,
            0.01553588f,  0.01947752f,  0.02399277f,  0.02910000f,
            0.03481485f,  0.04112016f,  0.04798504f,  0.05537861f,
            0.06327000f,  0.07163501f,  0.08046224f,  0.08973996f,
            0.09945645f,  0.1096000f,  0.1201674f,  0.1311145f,
            0.1423679f,  0.1538542f,  0.1655000f,  0.1772571f,
            0.1891400f,  0.2011694f,  0.2133658f,  0.2257499f,
            0.2383209f,  0.2510668f,  0.2639922f,  0.2771017f,
            0.2904000f,  0.3038912f,  0.3175726f,  0.3314384f,
            0.3454828f,  0.3597000f,  0.3740839f,  0.3886396f,
            0.4033784f,  0.4183115f,  0.4334499f,  0.4487953f,
            0.4643360f,  0.4800640f,  0.4959713f,  0.5120501f,
            0.5282959f,  0.5446916f,  0.5612094f,  0.5778215f,
            0.5945000f,  0.6112209f,  0.6279758f,  0.6447602f,
            0.6615697f,  0.6784000f,  0.6952392f,  0.7120586f,
            0.7288284f,  0.7455188f,  0.7621000f,  0.7785432f,
            0.7948256f,  0.8109264f,  0.8268248f,  0.8425000f,
            0.8579325f,  0.8730816f,  0.8878944f,  0.9023181f,
            0.9163000f,  0.9297995f,  0.9427984f,  0.9552776f,
            0.9672179f,  0.9786000f,  0.9893856f,  0.9995488f,
            1.0090892f,  1.0180064f,  1.0263000f,  1.0339827f,
            1.0409860f,  1.0471880f,  1.0524667f,  1.0567000f,
            1.0597944f,  1.0617992f,  1.0628068f,  1.0629096f,
            1.0622000f,  1.0607352f,  1.0584436f,  1.0552244f,
            1.0509768f,  1.0456000f,  1.0390369f,  1.0313608f,
            1.0226662f,  1.0130477f,  1.0026000f,  0.9913675f,
            0.9793314f,  0.9664916f,  0.9528479f,  0.9384000f,
            0.9231940f,  0.9072440f,  0.8905020f,  0.8729200f,
            0.8544499f,  0.8350840f,  0.8149460f,  0.7941860f,
            0.7729540f,  0.7514000f,  0.7295836f,  0.7075888f,
            0.6856022f,  0.6638104f,  0.6424000f,  0.6215149f,
            0.6011138f,  0.5811052f,  0.5613977f,  0.5419000f,
            0.5225995f,  0.5035464f,  0.4847436f,  0.4661939f,
            0.4479000f,  0.4298613f,  0.4120980f,  0.3946440f,
            0.3775333f,  0.3608000f,  0.3444563f,  0.3285168f,
            0.3130192f,  0.2980011f,  0.2835000f,  0.2695448f,
            0.2561184f,  0.2431896f,  0.2307272f,  0.2187000f,
            0.2070971f,  0.1959232f,  0.1851708f,  0.1748323f,
            0.1649000f,  0.1553667f,  0.1462300f,  0.1374900f,
            0.1291467f,  0.1212000f,  0.1136397f,  0.1064650f,
            0.09969044f,  0.09333061f,  0.08740000f,  0.08190096f,
            0.07680428f,  0.07207712f,  0.06768664f,  0.06360000f,
            0.05980685f,  0.05628216f,  0.05297104f,  0.04981861f,
            0.04677000f,  0.04378405f,  0.04087536f,  0.03807264f,
            0.03540461f,  0.03290000f,  0.03056419f,  0.02838056f,
            0.02634484f,  0.02445275f,  0.02270000f,  0.02108429f,
            0.01959988f,  0.01823732f,  0.01698717f,  0.01584000f,
            0.01479064f,  0.01383132f,  0.01294868f,  0.01212920f,
            0.01135916f,  0.01062935f,  0.009938846f,  0.009288422f,
            0.008678854f,  0.008110916f,  0.007582388f,  0.007088746f,
            0.006627313f,  0.006195408f,  0.005790346f,  0.005409826f,
            0.005052583f,  0.004717512f,  0.004403507f,  0.004109457f,
            0.003833913f,  0.003575748f,  0.003334342f,  0.003109075f,
            0.002899327f,  0.002704348f,  0.002523020f,  0.002354168f,
            0.002196616f,  0.002049190f,  0.001910960f,  0.001781438f,
            0.001660110f,  0.001546459f,  0.001439971f,  0.001340042f,
            0.001246275f,  0.001158471f,  0.001076430f,  0.0009999493f,
            0.0009287358f,  0.0008624332f,  0.0008007503f,  0.0007433960f,
            0.0006900786f,  0.0006405156f,  0.0005945021f,  0.0005518646f,
            0.0005124290f,  0.0004760213f,  0.0004424536f,  0.0004115117f,
            0.0003829814f,  0.0003566491f,  0.0003323011f,  0.0003097586f,
            0.0002888871f,  0.0002695394f,  0.0002515682f,  0.0002348261f,
            0.0002191710f,  0.0002045258f,  0.0001908405f,  0.0001780654f,
            0.0001661505f,  0.0001550236f,  0.0001446219f,  0.0001349098f,
            0.0001258520f,  0.0001174130f,  0.0001095515f,  0.0001022245f,
            0.00009539445f,  0.00008902390f,  0.00008307527f,  0.00007751269f,
            0.00007231304f,  0.00006745778f,  0.00006292844f,  0.00005870652f,
            0.00005477028f,  0.00005109918f,  0.00004767654f,  0.00004448567f,
            0.00004150994f,  0.00003873324f,  0.00003614203f,  0.00003372352f,
            0.00003146487f,  0.00002935326f,  0.00002737573f,  0.00002552433f,
            0.00002379376f,  0.00002217870f,  0.00002067383f,  0.00001927226f,
            0.00001796640f,  0.00001674991f,  0.00001561648f,  0.00001455977f,
            0.00001357387f,  0.00001265436f,  0.00001179723f,  0.00001099844f,
            0.00001025398f,  0.000009559646f,  0.000008912044f,  0.000008308358f,
            0.000007745769f,  0.000007221456f,  0.000006732475f,  0.000006276423f,
            0.000005851304f,  0.000005455118f,  0.000005085868f,  0.000004741466f,
            0.000004420236f,  0.000004120783f,  0.000003841716f,  0.000003581652f,
            0.000003339127f,  0.000003112949f,  0.000002902121f,  0.000002705645f,
            0.000002522525f,  0.000002351726f,  0.000002192415f,  0.000002043902f,
            0.000001905497f,  0.000001776509f,  0.000001656215f,  0.000001544022f,
            0.000001439440f, 0.000001341977f, 0.000001251141f 
        };
        public static float[] CIE_Y = new float[nCIE] {
            0.000003917000f,  0.000004393581f,  0.000004929604f,  0.000005532136f,
            0.000006208245f,  0.000006965000f,  0.000007813219f,  0.000008767336f,
            0.000009839844f,  0.00001104323f,  0.00001239000f,  0.00001388641f,
            0.00001555728f,  0.00001744296f,  0.00001958375f,  0.00002202000f,
            0.00002483965f,  0.00002804126f,  0.00003153104f,  0.00003521521f,
            0.00003900000f,  0.00004282640f,  0.00004691460f,  0.00005158960f,
            0.00005717640f,  0.00006400000f,  0.00007234421f,  0.00008221224f,
            0.00009350816f,  0.0001061361f,  0.0001200000f,  0.0001349840f,
            0.0001514920f,  0.0001702080f,  0.0001918160f,  0.0002170000f,
            0.0002469067f,  0.0002812400f,  0.0003185200f,  0.0003572667f,
            0.0003960000f,  0.0004337147f,  0.0004730240f,  0.0005178760f,
            0.0005722187f,  0.0006400000f,  0.0007245600f,  0.0008255000f,
            0.0009411600f,  0.001069880f,  0.001210000f,  0.001362091f,
            0.001530752f,  0.001720368f,  0.001935323f,  0.002180000f,
            0.002454800f,  0.002764000f,  0.003117800f,  0.003526400f,
            0.004000000f,  0.004546240f,  0.005159320f,  0.005829280f,
            0.006546160f,  0.007300000f,  0.008086507f,  0.008908720f,
            0.009767680f,  0.01066443f,  0.01160000f,  0.01257317f,
            0.01358272f,  0.01462968f,  0.01571509f,  0.01684000f,
            0.01800736f,  0.01921448f,  0.02045392f,  0.02171824f,
            0.02300000f,  0.02429461f,  0.02561024f,  0.02695857f,
            0.02835125f,  0.02980000f,  0.03131083f,  0.03288368f,
            0.03452112f,  0.03622571f,  0.03800000f,  0.03984667f,
            0.04176800f,  0.04376600f,  0.04584267f,  0.04800000f,
            0.05024368f,  0.05257304f,  0.05498056f,  0.05745872f,
            0.06000000f,  0.06260197f,  0.06527752f,  0.06804208f,
            0.07091109f,  0.07390000f,  0.07701600f,  0.08026640f,
            0.08366680f,  0.08723280f,  0.09098000f,  0.09491755f,
            0.09904584f,  0.1033674f,  0.1078846f,  0.1126000f,
            0.1175320f,  0.1226744f,  0.1279928f,  0.1334528f,
            0.1390200f,  0.1446764f,  0.1504693f,  0.1564619f,
            0.1627177f,  0.1693000f,  0.1762431f,  0.1835581f,
            0.1912735f,  0.1994180f,  0.2080200f,  0.2171199f,
            0.2267345f,  0.2368571f,  0.2474812f,  0.2586000f,
            0.2701849f,  0.2822939f,  0.2950505f,  0.3085780f,
            0.3230000f,  0.3384021f,  0.3546858f,  0.3716986f,
            0.3892875f,  0.4073000f,  0.4256299f,  0.4443096f,
            0.4633944f,  0.4829395f,  0.5030000f,  0.5235693f,
            0.5445120f,  0.5656900f,  0.5869653f,  0.6082000f,
            0.6293456f,  0.6503068f,  0.6708752f,  0.6908424f,
            0.7100000f,  0.7281852f,  0.7454636f,  0.7619694f,
            0.7778368f,  0.7932000f,  0.8081104f,  0.8224962f,
            0.8363068f,  0.8494916f,  0.8620000f,  0.8738108f,
            0.8849624f,  0.8954936f,  0.9054432f,  0.9148501f,
            0.9237348f,  0.9320924f,  0.9399226f,  0.9472252f,
            0.9540000f,  0.9602561f,  0.9660074f,  0.9712606f,
            0.9760225f,  0.9803000f,  0.9840924f,  0.9874812f,
            0.9903128f,  0.9928116f,  0.9949501f,  0.9967108f,
            0.9980983f,  0.9991120f,  0.9997482f,  1.0000000f,
            0.9998567f,  0.9993046f,  0.9983255f,  0.9968987f,
            0.9950000f,  0.9926005f,  0.9897426f,  0.9864444f,
            0.9827241f,  0.9786000f,  0.9740837f,  0.9691712f,
            0.9638568f,  0.9581349f,  0.9520000f,  0.9454504f,
            0.9384992f,  0.9311628f,  0.9234576f,  0.9154000f,
            0.9070064f,  0.8982772f,  0.8892048f,  0.8797816f,
            0.8700000f,  0.8598613f,  0.8493920f,  0.8386220f,
            0.8275813f,  0.8163000f,  0.8047947f,  0.7930820f,
            0.7811920f,  0.7691547f,  0.7570000f,  0.7447541f,
            0.7324224f,  0.7200036f,  0.7074965f,  0.6949000f,
            0.6822192f,  0.6694716f,  0.6566744f,  0.6438448f,
            0.6310000f,  0.6181555f,  0.6053144f,  0.5924756f,
            0.5796379f,  0.5668000f,  0.5539611f,  0.5411372f,
            0.5283528f,  0.5156323f,  0.5030000f,  0.4904688f,
            0.4780304f,  0.4656776f,  0.4534032f,  0.4412000f,
            0.4290800f,  0.4170360f,  0.4050320f,  0.3930320f,
            0.3810000f,  0.3689184f,  0.3568272f,  0.3447768f,
            0.3328176f,  0.3210000f,  0.3093381f,  0.2978504f,
            0.2865936f,  0.2756245f,  0.2650000f,  0.2547632f,
            0.2448896f,  0.2353344f,  0.2260528f,  0.2170000f,
            0.2081616f,  0.1995488f,  0.1911552f,  0.1829744f,
            0.1750000f,  0.1672235f,  0.1596464f,  0.1522776f,
            0.1451259f,  0.1382000f,  0.1315003f,  0.1250248f,
            0.1187792f,  0.1127691f,  0.1070000f,  0.1014762f,
            0.09618864f,  0.09112296f,  0.08626485f,  0.08160000f,
            0.07712064f,  0.07282552f,  0.06871008f,  0.06476976f,
            0.06100000f,  0.05739621f,  0.05395504f,  0.05067376f,
            0.04754965f,  0.04458000f,  0.04175872f,  0.03908496f,
            0.03656384f,  0.03420048f,  0.03200000f,  0.02996261f,
            0.02807664f,  0.02632936f,  0.02470805f,  0.02320000f,
            0.02180077f,  0.02050112f,  0.01928108f,  0.01812069f,
            0.01700000f,  0.01590379f,  0.01483718f,  0.01381068f,
            0.01283478f,  0.01192000f,  0.01106831f,  0.01027339f,
            0.009533311f,  0.008846157f,  0.008210000f,  0.007623781f,
            0.007085424f,  0.006591476f,  0.006138485f,  0.005723000f,
            0.005343059f,  0.004995796f,  0.004676404f,  0.004380075f,
            0.004102000f,  0.003838453f,  0.003589099f,  0.003354219f,
            0.003134093f,  0.002929000f,  0.002738139f,  0.002559876f,
            0.002393244f,  0.002237275f,  0.002091000f,  0.001953587f,
            0.001824580f,  0.001703580f,  0.001590187f,  0.001484000f,
            0.001384496f,  0.001291268f,  0.001204092f,  0.001122744f,
            0.001047000f,  0.0009765896f,  0.0009111088f,  0.0008501332f,
            0.0007932384f,  0.0007400000f,  0.0006900827f,  0.0006433100f,
            0.0005994960f,  0.0005584547f,  0.0005200000f,  0.0004839136f,
            0.0004500528f,  0.0004183452f,  0.0003887184f,  0.0003611000f,
            0.0003353835f,  0.0003114404f,  0.0002891656f,  0.0002684539f,
            0.0002492000f,  0.0002313019f,  0.0002146856f,  0.0001992884f,
            0.0001850475f,  0.0001719000f,  0.0001597781f,  0.0001486044f,
            0.0001383016f,  0.0001287925f,  0.0001200000f,  0.0001118595f,
            0.0001043224f,  0.00009733560f,  0.00009084587f,  0.00008480000f,
            0.00007914667f,  0.00007385800f,  0.00006891600f,  0.00006430267f,
            0.00006000000f,  0.00005598187f,  0.00005222560f,  0.00004871840f,
            0.00004544747f,  0.00004240000f,  0.00003956104f,  0.00003691512f,
            0.00003444868f,  0.00003214816f,  0.00003000000f,  0.00002799125f,
            0.00002611356f,  0.00002436024f,  0.00002272461f,  0.00002120000f,
            0.00001977855f,  0.00001845285f,  0.00001721687f,  0.00001606459f,
            0.00001499000f,  0.00001398728f,  0.00001305155f,  0.00001217818f,
            0.00001136254f,  0.00001060000f,  0.000009885877f,  0.000009217304f,
            0.000008592362f,  0.000008009133f,  0.000007465700f,  0.000006959567f,
            0.000006487995f,  0.000006048699f,  0.000005639396f,  0.000005257800f,
            0.000004901771f,  0.000004569720f,  0.000004260194f,  0.000003971739f,
            0.000003702900f,  0.000003452163f,  0.000003218302f,  0.000003000300f,
            0.000002797139f,  0.000002607800f,  0.000002431220f,  0.000002266531f,
            0.000002113013f,  0.000001969943f,  0.000001836600f,  0.000001712230f,
            0.000001596228f,  0.000001488090f,  0.000001387314f,  0.000001293400f,
            0.000001205820f,  0.000001124143f,  0.000001048009f,  0.0000009770578f,
            0.0000009109300f,  0.0000008492513f,  0.0000007917212f,  0.0000007380904f,
            0.0000006881098f,  0.0000006415300f,  0.0000005980895f,  0.0000005575746f,
            0.0000005198080f, 0.0000004846123f, 0.0000004518100f 
        };
        public static float[] CIE_Z = new float[nCIE] {
            0.0006061000f,  0.0006808792f,  0.0007651456f,  0.0008600124f,
            0.0009665928f,  0.001086000f,  0.001220586f,  0.001372729f,
            0.001543579f,  0.001734286f,  0.001946000f,  0.002177777f,
            0.002435809f,  0.002731953f,  0.003078064f,  0.003486000f,
            0.003975227f,  0.004540880f,  0.005158320f,  0.005802907f,
            0.006450001f,  0.007083216f,  0.007745488f,  0.008501152f,
            0.009414544f,  0.01054999f,  0.01196580f,  0.01365587f,
            0.01558805f,  0.01773015f,  0.02005001f,  0.02251136f,
            0.02520288f,  0.02827972f,  0.03189704f,  0.03621000f,
            0.04143771f,  0.04750372f,  0.05411988f,  0.06099803f,
            0.06785001f,  0.07448632f,  0.08136156f,  0.08915364f,
            0.09854048f,  0.1102000f,  0.1246133f,  0.1417017f,
            0.1613035f,  0.1832568f,  0.2074000f,  0.2336921f,
            0.2626114f,  0.2947746f,  0.3307985f,  0.3713000f,
            0.4162091f,  0.4654642f,  0.5196948f,  0.5795303f,
            0.6456000f,  0.7184838f,  0.7967133f,  0.8778459f,
            0.9594390f,  1.0390501f,  1.1153673f,  1.1884971f,
            1.2581233f,  1.3239296f,  1.3856000f,  1.4426352f,
            1.4948035f,  1.5421903f,  1.5848807f,  1.6229600f,
            1.6564048f,  1.6852959f,  1.7098745f,  1.7303821f,
            1.7470600f,  1.7600446f,  1.7696233f,  1.7762637f,
            1.7804334f,  1.7826000f,  1.7829682f,  1.7816998f,
            1.7791982f,  1.7758671f,  1.7721100f,  1.7682589f,
            1.7640390f,  1.7589438f,  1.7524663f,  1.7441000f,
            1.7335595f,  1.7208581f,  1.7059369f,  1.6887372f,
            1.6692000f,  1.6475287f,  1.6234127f,  1.5960223f,
            1.5645280f,  1.5281000f,  1.4861114f,  1.4395215f,
            1.3898799f,  1.3387362f,  1.2876400f,  1.2374223f,
            1.1878243f,  1.1387611f,  1.0901480f,  1.0419000f,
            0.9941976f,  0.9473473f,  0.9014531f,  0.8566193f,
            0.8129501f,  0.7705173f,  0.7294448f,  0.6899136f,
            0.6521049f,  0.6162000f,  0.5823286f,  0.5504162f,
            0.5203376f,  0.4919673f,  0.4651800f,  0.4399246f,
            0.4161836f,  0.3938822f,  0.3729459f,  0.3533000f,
            0.3348578f,  0.3175521f,  0.3013375f,  0.2861686f,
            0.2720000f,  0.2588171f,  0.2464838f,  0.2347718f,
            0.2234533f,  0.2123000f,  0.2011692f,  0.1901196f,
            0.1792254f,  0.1685608f,  0.1582000f,  0.1481383f,
            0.1383758f,  0.1289942f,  0.1200751f,  0.1117000f,
            0.1039048f,  0.09666748f,  0.08998272f,  0.08384531f,
            0.07824999f,  0.07320899f,  0.06867816f,  0.06456784f,
            0.06078835f,  0.05725001f,  0.05390435f,  0.05074664f,
            0.04775276f,  0.04489859f,  0.04216000f,  0.03950728f,
            0.03693564f,  0.03445836f,  0.03208872f,  0.02984000f,
            0.02771181f,  0.02569444f,  0.02378716f,  0.02198925f,
            0.02030000f,  0.01871805f,  0.01724036f,  0.01586364f,
            0.01458461f,  0.01340000f,  0.01230723f,  0.01130188f,
            0.01037792f,  0.009529306f,  0.008749999f,  0.008035200f,
            0.007381600f,  0.006785400f,  0.006242800f,  0.005749999f,
            0.005303600f,  0.004899800f,  0.004534200f,  0.004202400f,
            0.003900000f,  0.003623200f,  0.003370600f,  0.003141400f,
            0.002934800f,  0.002749999f,  0.002585200f,  0.002438600f,
            0.002309400f,  0.002196800f,  0.002100000f,  0.002017733f,
            0.001948200f,  0.001889800f,  0.001840933f,  0.001800000f,
            0.001766267f,  0.001737800f,  0.001711200f,  0.001683067f,
            0.001650001f,  0.001610133f,  0.001564400f,  0.001513600f,
            0.001458533f,  0.001400000f,  0.001336667f,  0.001270000f,
            0.001205000f,  0.001146667f,  0.001100000f,  0.001068800f,
            0.001049400f,  0.001035600f,  0.001021200f,  0.001000000f,
            0.0009686400f,  0.0009299200f,  0.0008868800f,  0.0008425600f,
            0.0008000000f,  0.0007609600f,  0.0007236800f,  0.0006859200f,
            0.0006454400f,  0.0006000000f,  0.0005478667f,  0.0004916000f,
            0.0004354000f,  0.0003834667f,  0.0003400000f,  0.0003072533f,
            0.0002831600f,  0.0002654400f,  0.0002518133f,  0.0002400000f,
            0.0002295467f,  0.0002206400f,  0.0002119600f,  0.0002021867f,
            0.0001900000f,  0.0001742133f,  0.0001556400f,  0.0001359600f,
            0.0001168533f,  0.0001000000f,  0.00008613333f,  0.00007460000f,
            0.00006500000f,  0.00005693333f,  0.00004999999f,  0.00004416000f,
            0.00003948000f,  0.00003572000f,  0.00003264000f,  0.00003000000f,
            0.00002765333f,  0.00002556000f,  0.00002364000f,  0.00002181333f,
            0.00002000000f,  0.00001813333f,  0.00001620000f,  0.00001420000f,
            0.00001213333f,  0.00001000000f,  0.000007733333f,  0.000005400000f,
            0.000003200000f,  0.000001333333f,  0.000000000000f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f,  0.0f,
            0.0f,  0.0f,  0.0f 
        };


        public static float[] CIE_lambda = new[] {
            360f, 361f, 362f, 363f, 364f, 365f, 366f, 367f, 368f, 369f, 370f, 371f, 372f, 373f,
            374f, 375f, 376f, 377f, 378f, 379f, 380f, 381f, 382f, 383f, 384f, 385f, 386f, 387f,
            388f, 389f, 390f, 391f, 392f, 393f, 394f, 395f, 396f, 397f, 398f, 399f, 400f, 401f,
            402f, 403f, 404f, 405f, 406f, 407f, 408f, 409f, 410f, 411f, 412f, 413f, 414f, 415f,
            416f, 417f, 418f, 419f, 420f, 421f, 422f, 423f, 424f, 425f, 426f, 427f, 428f, 429f,
            430f, 431f, 432f, 433f, 434f, 435f, 436f, 437f, 438f, 439f, 440f, 441f, 442f, 443f,
            444f, 445f, 446f, 447f, 448f, 449f, 450f, 451f, 452f, 453f, 454f, 455f, 456f, 457f,
            458f, 459f, 460f, 461f, 462f, 463f, 464f, 465f, 466f, 467f, 468f, 469f, 470f, 471f,
            472f, 473f, 474f, 475f, 476f, 477f, 478f, 479f, 480f, 481f, 482f, 483f, 484f, 485f,
            486f, 487f, 488f, 489f, 490f, 491f, 492f, 493f, 494f, 495f, 496f, 497f, 498f, 499f,
            500f, 501f, 502f, 503f, 504f, 505f, 506f, 507f, 508f, 509f, 510f, 511f, 512f, 513f,
            514f, 515f, 516f, 517f, 518f, 519f, 520f, 521f, 522f, 523f, 524f, 525f, 526f, 527f,
            528f, 529f, 530f, 531f, 532f, 533f, 534f, 535f, 536f, 537f, 538f, 539f, 540f, 541f,
            542f, 543f, 544f, 545f, 546f, 547f, 548f, 549f, 550f, 551f, 552f, 553f, 554f, 555f,
            556f, 557f, 558f, 559f, 560f, 561f, 562f, 563f, 564f, 565f, 566f, 567f, 568f, 569f,
            570f, 571f, 572f, 573f, 574f, 575f, 576f, 577f, 578f, 579f, 580f, 581f, 582f, 583f,
            584f, 585f, 586f, 587f, 588f, 589f, 590f, 591f, 592f, 593f, 594f, 595f, 596f, 597f,
            598f, 599f, 600f, 601f, 602f, 603f, 604f, 605f, 606f, 607f, 608f, 609f, 610f, 611f,
            612f, 613f, 614f, 615f, 616f, 617f, 618f, 619f, 620f, 621f, 622f, 623f, 624f, 625f,
            626f, 627f, 628f, 629f, 630f, 631f, 632f, 633f, 634f, 635f, 636f, 637f, 638f, 639f,
            640f, 641f, 642f, 643f, 644f, 645f, 646f, 647f, 648f, 649f, 650f, 651f, 652f, 653f,
            654f, 655f, 656f, 657f, 658f, 659f, 660f, 661f, 662f, 663f, 664f, 665f, 666f, 667f,
            668f, 669f, 670f, 671f, 672f, 673f, 674f, 675f, 676f, 677f, 678f, 679f, 680f, 681f,
            682f, 683f, 684f, 685f, 686f, 687f, 688f, 689f, 690f, 691f, 692f, 693f, 694f, 695f,
            696f, 697f, 698f, 699f, 700f, 701f, 702f, 703f, 704f, 705f, 706f, 707f, 708f, 709f,
            710f, 711f, 712f, 713f, 714f, 715f, 716f, 717f, 718f, 719f, 720f, 721f, 722f, 723f,
            724f, 725f, 726f, 727f, 728f, 729f, 730f, 731f, 732f, 733f, 734f, 735f, 736f, 737f,
            738f, 739f, 740f, 741f, 742f, 743f, 744f, 745f, 746f, 747f, 748f, 749f, 750f, 751f,
            752f, 753f, 754f, 755f, 756f, 757f, 758f, 759f, 760f, 761f, 762f, 763f, 764f, 765f,
            766f, 767f, 768f, 769f, 770f, 771f, 772f, 773f, 774f, 775f, 776f, 777f, 778f, 779f,
            780f, 781f, 782f, 783f, 784f, 785f, 786f, 787f, 788f, 789f, 790f, 791f, 792f, 793f,
            794f, 795f, 796f, 797f, 798f, 799f, 800f, 801f, 802f, 803f, 804f, 805f, 806f, 807f,
            808f, 809f, 810f, 811f, 812f, 813f, 814f, 815f, 816f, 817f, 818f, 819f, 820f, 821f,
            822f, 823f, 824f, 825f, 826f, 827f, 828f, 829f, 830f };


        public static float[] RGB2SpectLambda = new[] {
            380.000000f, 390.967743f, 401.935486f, 412.903229f, 423.870972f,
            434.838715f,
            445.806458f, 456.774200f, 467.741943f, 478.709686f, 489.677429f,
            500.645172f,
            511.612915f, 522.580627f, 533.548340f, 544.516052f, 555.483765f,
            566.451477f,
            577.419189f, 588.386902f, 599.354614f, 610.322327f, 621.290039f,
            632.257751f,
            643.225464f, 654.193176f, 665.160889f, 676.128601f, 687.096313f,
            698.064026f,
            709.031738f, 720.000000f
        };

        public static float[] RGBRefl2SpectWhite = new[]       {
            1.0618958571272863e+00f,   1.0615019980348779e+00f,
            1.0614335379927147e+00f,   1.0622711654692485e+00f,
            1.0622036218416742e+00f,   1.0625059965187085e+00f,
            1.0623938486985884e+00f,   1.0624706448043137e+00f,
            1.0625048144827762e+00f,   1.0624366131308856e+00f,
            1.0620694238892607e+00f,   1.0613167586932164e+00f,
            1.0610334029377020e+00f,   1.0613868564828413e+00f,
            1.0614215366116762e+00f,   1.0620336151299086e+00f,
            1.0625497454805051e+00f,   1.0624317487992085e+00f,
            1.0625249140554480e+00f,   1.0624277664486914e+00f,
            1.0624749854090769e+00f,   1.0625538581025402e+00f,
            1.0625326910104864e+00f,   1.0623922312225325e+00f,
            1.0623650980354129e+00f,   1.0625256476715284e+00f,
            1.0612277619533155e+00f,   1.0594262608698046e+00f,
            1.0599810758292072e+00f,   1.0602547314449409e+00f,
            1.0601263046243634e+00f,   1.0606565756823634e+00f };

        public static float[] RGBRefl2SpectCyan = new[]   {
            1.0414628021426751e+00f,   1.0328661533771188e+00f,
            1.0126146228964314e+00f,   1.0350460524836209e+00f,
            1.0078661447098567e+00f,   1.0422280385081280e+00f,
            1.0442596738499825e+00f,   1.0535238290294409e+00f,
            1.0180776226938120e+00f,   1.0442729908727713e+00f,
            1.0529362541920750e+00f,   1.0537034271160244e+00f,
            1.0533901869215969e+00f,   1.0537782700979574e+00f,
            1.0527093770467102e+00f,   1.0530449040446797e+00f,
            1.0550554640191208e+00f,   1.0553673610724821e+00f,
            1.0454306634683976e+00f,   6.2348950639230805e-01f,
            1.8038071613188977e-01f,  -7.6303759201984539e-03f,
            -1.5217847035781367e-04f,  -7.5102257347258311e-03f,
            -2.1708639328491472e-03f,   6.5919466602369636e-04f,
            1.2278815318539780e-02f,  -4.4669775637208031e-03f,
            1.7119799082865147e-02f,   4.9211089759759801e-03f,
            5.8762925143334985e-03f,   2.5259399415550079e-02f };

        public static float[] RGBRefl2SpectMagenta = new[]  {
            9.9422138151236850e-01f,   9.8986937122975682e-01f,
            9.8293658286116958e-01f,   9.9627868399859310e-01f,
            1.0198955019000133e+00f,   1.0166395501210359e+00f,
            1.0220913178757398e+00f,   9.9651666040682441e-01f,
            1.0097766178917882e+00f,   1.0215422470827016e+00f,
            6.4031953387790963e-01f,   2.5012379477078184e-03f,
            6.5339939555769944e-03f,   2.8334080462675826e-03f,
            -5.1209675389074505e-11f,  -9.0592291646646381e-03f,
            3.3936718323331200e-03f,  -3.0638741121828406e-03f,
            2.2203936168286292e-01f,   6.3141140024811970e-01f,
            9.7480985576500956e-01f,   9.7209562333590571e-01f,
            1.0173770302868150e+00f,   9.9875194322734129e-01f,
            9.4701725739602238e-01f,   8.5258623154354796e-01f,
            9.4897798581660842e-01f,   9.4751876096521492e-01f,
            9.9598944191059791e-01f,   8.6301351503809076e-01f,
            8.9150987853523145e-01f,   8.4866492652845082e-01f };

        public static float[] RGBRefl2SpectYellow = new[]  {
            5.5740622924920873e-03f,  -4.7982831631446787e-03f,
            -5.2536564298613798e-03f,  -6.4571480044499710e-03f,
            -5.9693514658007013e-03f,  -2.1836716037686721e-03f,
            1.6781120601055327e-02f,   9.6096355429062641e-02f,
            2.1217357081986446e-01f,   3.6169133290685068e-01f,
            5.3961011543232529e-01f,   7.4408810492171507e-01f,
            9.2209571148394054e-01f,   1.0460304298411225e+00f,
            1.0513824989063714e+00f,   1.0511991822135085e+00f,
            1.0510530911991052e+00f,   1.0517397230360510e+00f,
            1.0516043086790485e+00f,   1.0511944032061460e+00f,
            1.0511590325868068e+00f,   1.0516612465483031e+00f,
            1.0514038526836869e+00f,   1.0515941029228475e+00f,
            1.0511460436960840e+00f,   1.0515123758830476e+00f,
            1.0508871369510702e+00f,   1.0508923708102380e+00f,
            1.0477492815668303e+00f,   1.0493272144017338e+00f,
            1.0435963333422726e+00f,   1.0392280772051465e+00f };

        public static float[] RGBRefl2SpectRed = new[]   {
            1.6575604867086180e-01f,   1.1846442802747797e-01f,
            1.2408293329637447e-01f,   1.1371272058349924e-01f,
            7.8992434518899132e-02f,   3.2205603593106549e-02f,
            -1.0798365407877875e-02f,   1.8051975516730392e-02f,
            5.3407196598730527e-03f,   1.3654918729501336e-02f,
            -5.9564213545642841e-03f,  -1.8444365067353252e-03f,
            -1.0571884361529504e-02f,  -2.9375521078000011e-03f,
            -1.0790476271835936e-02f,  -8.0224306697503633e-03f,
            -2.2669167702495940e-03f,   7.0200240494706634e-03f,
            -8.1528469000299308e-03f,   6.0772866969252792e-01f,
            9.8831560865432400e-01f,   9.9391691044078823e-01f,
            1.0039338994753197e+00f,   9.9234499861167125e-01f,
            9.9926530858855522e-01f,   1.0084621557617270e+00f,
            9.8358296827441216e-01f,   1.0085023660099048e+00f,
            9.7451138326568698e-01f,   9.8543269570059944e-01f,
            9.3495763980962043e-01f,   9.8713907792319400e-01f };

        public static float[] RGBRefl2SpectGreen = new[]   {
            2.6494153587602255e-03f,  -5.0175013429732242e-03f,
            -1.2547236272489583e-02f,  -9.4554964308388671e-03f,
            -1.2526086181600525e-02f,  -7.9170697760437767e-03f,
            -7.9955735204175690e-03f,  -9.3559433444469070e-03f,
            6.5468611982999303e-02f,   3.9572875517634137e-01f,
            7.5244022299886659e-01f,   9.6376478690218559e-01f,
            9.9854433855162328e-01f,   9.9992977025287921e-01f,
            9.9939086751140449e-01f,   9.9994372267071396e-01f,
            9.9939121813418674e-01f,   9.9911237310424483e-01f,
            9.6019584878271580e-01f,   6.3186279338432438e-01f,
            2.5797401028763473e-01f,   9.4014888527335638e-03f,
            -3.0798345608649747e-03f,  -4.5230367033685034e-03f,
            -6.8933410388274038e-03f,  -9.0352195539015398e-03f,
            -8.5913667165340209e-03f,  -8.3690869120289398e-03f,
            -7.8685832338754313e-03f,  -8.3657578711085132e-06f,
            5.4301225442817177e-03f,  -2.7745589759259194e-03f };

        public static float[] RGBRefl2SpectBlue = new[]   {
            9.9209771469720676e-01f,   9.8876426059369127e-01f,
            9.9539040744505636e-01f,   9.9529317353008218e-01f,
            9.9181447411633950e-01f,   1.0002584039673432e+00f,
            9.9968478437342512e-01f,   9.9988120766657174e-01f,
            9.8504012146370434e-01f,   7.9029849053031276e-01f,
            5.6082198617463974e-01f,   3.3133458513996528e-01f,
            1.3692410840839175e-01f,   1.8914906559664151e-02f,
            -5.1129770932550889e-06f,  -4.2395493167891873e-04f,
            -4.1934593101534273e-04f,   1.7473028136486615e-03f,
            3.7999160177631316e-03f,  -5.5101474906588642e-04f,
            -4.3716662898480967e-05f,   7.5874501748732798e-03f,
            2.5795650780554021e-02f,   3.8168376532500548e-02f,
            4.9489586408030833e-02f,   4.9595992290102905e-02f,
            4.9814819505812249e-02f,   3.9840911064978023e-02f,
            3.0501024937233868e-02f,   2.1243054765241080e-02f,
            6.9596532104356399e-03f,   4.1733649330980525e-03f };
        public static float[] RGBIllum2SpectWhite = new[]     {
            1.1565232050369776e+00f,   1.1567225000119139e+00f,
            1.1566203150243823e+00f,   1.1555782088080084e+00f,
            1.1562175509215700e+00f,   1.1567674012207332e+00f,
            1.1568023194808630e+00f,   1.1567677445485520e+00f,
            1.1563563182952830e+00f,   1.1567054702510189e+00f,
            1.1565134139372772e+00f,   1.1564336176499312e+00f,
            1.1568023181530034e+00f,   1.1473147688514642e+00f,
            1.1339317140561065e+00f,   1.1293876490671435e+00f,
            1.1290515328639648e+00f,   1.0504864823782283e+00f,
            1.0459696042230884e+00f,   9.9366687168595691e-01f,
            9.5601669265393940e-01f,   9.2467482033511805e-01f,
            9.1499944702051761e-01f,   8.9939467658453465e-01f,
            8.9542520751331112e-01f,   8.8870566693814745e-01f,
            8.8222843814228114e-01f,   8.7998311373826676e-01f,
            8.7635244612244578e-01f,   8.8000368331709111e-01f,
            8.8065665428441120e-01f,   8.8304706460276905e-01f };

        public static float[] RGBIllum2SpectCyan = new[]  {
            1.1334479663682135e+00f,   1.1266762330194116e+00f,
            1.1346827504710164e+00f,   1.1357395805744794e+00f,
            1.1356371830149636e+00f,   1.1361152989346193e+00f,
            1.1362179057706772e+00f,   1.1364819652587022e+00f,
            1.1355107110714324e+00f,   1.1364060941199556e+00f,
            1.1360363621722465e+00f,   1.1360122641141395e+00f,
            1.1354266882467030e+00f,   1.1363099407179136e+00f,
            1.1355450412632506e+00f,   1.1353732327376378e+00f,
            1.1349496420726002e+00f,   1.1111113947168556e+00f,
            9.0598740429727143e-01f,   6.1160780787465330e-01f,
            2.9539752170999634e-01f,   9.5954200671150097e-02f,
            -1.1650792030826267e-02f,  -1.2144633073395025e-02f,
            -1.1148167569748318e-02f,  -1.1997606668458151e-02f,
            -5.0506855475394852e-03f,  -7.9982745819542154e-03f,
            -9.4722817708236418e-03f,  -5.5329541006658815e-03f,
            -4.5428914028274488e-03f,  -1.2541015360921132e-02f };

        public static float[] RGBIllum2SpectMagenta = new[]  {
            1.0371892935878366e+00f,   1.0587542891035364e+00f,
            1.0767271213688903e+00f,   1.0762706844110288e+00f,
            1.0795289105258212e+00f,   1.0743644742950074e+00f,
            1.0727028691194342e+00f,   1.0732447452056488e+00f,
            1.0823760816041414e+00f,   1.0840545681409282e+00f,
            9.5607567526306658e-01f,   5.5197896855064665e-01f,
            8.4191094887247575e-02f,   8.7940070557041006e-05f,
            -2.3086408335071251e-03f,  -1.1248136628651192e-03f,
            -7.7297612754989586e-11f,  -2.7270769006770834e-04f,
            1.4466473094035592e-02f,   2.5883116027169478e-01f,
            5.2907999827566732e-01f,   9.0966624097105164e-01f,
            1.0690571327307956e+00f,   1.0887326064796272e+00f,
            1.0637622289511852e+00f,   1.0201812918094260e+00f,
            1.0262196688979945e+00f,   1.0783085560613190e+00f,
            9.8333849623218872e-01f,   1.0707246342802621e+00f,
            1.0634247770423768e+00f,   1.0150875475729566e+00f };

        public static float[] RGBIllum2SpectYellow = new[]  {
            2.7756958965811972e-03f,   3.9673820990646612e-03f,
            -1.4606936788606750e-04f,   3.6198394557748065e-04f,
            -2.5819258699309733e-04f,  -5.0133191628082274e-05f,
            -2.4437242866157116e-04f,  -7.8061419948038946e-05f,
            4.9690301207540921e-02f,   4.8515973574763166e-01f,
            1.0295725854360589e+00f,   1.0333210878457741e+00f,
            1.0368102644026933e+00f,   1.0364884018886333e+00f,
            1.0365427939411784e+00f,   1.0368595402854539e+00f,
            1.0365645405660555e+00f,   1.0363938240707142e+00f,
            1.0367205578770746e+00f,   1.0365239329446050e+00f,
            1.0361531226427443e+00f,   1.0348785007827348e+00f,
            1.0042729660717318e+00f,   8.4218486432354278e-01f,
            7.3759394894801567e-01f,   6.5853154500294642e-01f,
            6.0531682444066282e-01f,   5.9549794132420741e-01f,
            5.9419261278443136e-01f,   5.6517682326634266e-01f,
            5.6061186014968556e-01f,   5.8228610381018719e-01f };

        public static float[] RGBIllum2SpectRed = new[]  {
            5.4711187157291841e-02f,   5.5609066498303397e-02f,
            6.0755873790918236e-02f,   5.6232948615962369e-02f,
            4.6169940535708678e-02f,   3.8012808167818095e-02f,
            2.4424225756670338e-02f,   3.8983580581592181e-03f,
            -5.6082252172734437e-04f,   9.6493871255194652e-04f,
            3.7341198051510371e-04f,  -4.3367389093135200e-04f,
            -9.3533962256892034e-05f,  -1.2354967412842033e-04f,
            -1.4524548081687461e-04f,  -2.0047691915543731e-04f,
            -4.9938587694693670e-04f,   2.7255083540032476e-02f,
            1.6067405906297061e-01f,   3.5069788873150953e-01f,
            5.7357465538418961e-01f,   7.6392091890718949e-01f,
            8.9144466740381523e-01f,   9.6394609909574891e-01f,
            9.8879464276016282e-01f,   9.9897449966227203e-01f,
            9.8605140403564162e-01f,   9.9532502805345202e-01f,
            9.7433478377305371e-01f,   9.9134364616871407e-01f,
            9.8866287772174755e-01f,   9.9713856089735531e-01f };

        public static float[] RGBIllum2SpectGreen = new[]  {
            2.5168388755514630e-02f,   3.9427438169423720e-02f,
            6.2059571596425793e-03f,   7.1120859807429554e-03f,
            2.1760044649139429e-04f,   7.3271839984290210e-12f,
            -2.1623066217181700e-02f,   1.5670209409407512e-02f,
            2.8019603188636222e-03f,   3.2494773799897647e-01f,
            1.0164917292316602e+00f,   1.0329476657890369e+00f,
            1.0321586962991549e+00f,   1.0358667411948619e+00f,
            1.0151235476834941e+00f,   1.0338076690093119e+00f,
            1.0371372378155013e+00f,   1.0361377027692558e+00f,
            1.0229822432557210e+00f,   9.6910327335652324e-01f,
            -5.1785923899878572e-03f,   1.1131261971061429e-03f,
            6.6675503033011771e-03f,   7.4024315686001957e-04f,
            2.1591567633473925e-02f,   5.1481620056217231e-03f,
            1.4561928645728216e-03f,   1.6414511045291513e-04f,
            -6.4630764968453287e-03f,   1.0250854718507939e-02f,
            4.2387394733956134e-02f,   2.1252716926861620e-02f };

        public static float[] RGBIllum2SpectBlue = new[]  {
            1.0570490759328752e+00f,   1.0538466912851301e+00f,
            1.0550494258140670e+00f,   1.0530407754701832e+00f,
            1.0579930596460185e+00f,   1.0578439494812371e+00f,
            1.0583132387180239e+00f,   1.0579712943137616e+00f,
            1.0561884233578465e+00f,   1.0571399285426490e+00f,
            1.0425795187752152e+00f,   3.2603084374056102e-01f,
            -1.9255628442412243e-03f,  -1.2959221137046478e-03f,
            -1.4357356276938696e-03f,  -1.2963697250337886e-03f,
            -1.9227081162373899e-03f,   1.2621152526221778e-03f,
            -1.6095249003578276e-03f,  -1.3029983817879568e-03f,
            -1.7666600873954916e-03f,  -1.2325281140280050e-03f,
            1.0316809673254932e-02f,   3.1284512648354357e-02f,
            8.8773879881746481e-02f,   1.3873621740236541e-01f,
            1.5535067531939065e-01f,   1.4878477178237029e-01f,
            1.6624255403475907e-01f,   1.6997613960634927e-01f,
            1.5769743995852967e-01f,   1.9069090525482305e-01f };

        //81,24
        public static double[][] macbethSpectra = new[] {
  /* 380 nm */ new [] { 0.048, 0.103, 0.113, 0.048, 0.123, 0.110, 0.053, 0.099, 0.096, 0.101, 0.056, 0.060, 0.069, 0.055, 0.052, 0.054, 0.118, 0.093, 0.153, 0.150, 0.138, 0.113, 0.074, 0.032 },
  /* 385 nm */ new [] { 0.051, 0.120, 0.138, 0.049, 0.152, 0.133, 0.054, 0.120, 0.108, 0.115, 0.058, 0.061, 0.081, 0.056, 0.052, 0.053, 0.142, 0.110, 0.189, 0.184, 0.167, 0.131, 0.079, 0.033 },
  /* 390 nm */ new [] { 0.055, 0.141, 0.174, 0.049, 0.197, 0.167, 0.054, 0.150, 0.123, 0.135, 0.059, 0.063, 0.096, 0.057, 0.052, 0.054, 0.179, 0.134, 0.245, 0.235, 0.206, 0.150, 0.084, 0.033 },
  /* 395 nm */ new [] { 0.060, 0.163, 0.219, 0.049, 0.258, 0.208, 0.054, 0.189, 0.135, 0.157, 0.059, 0.064, 0.114, 0.058, 0.052, 0.053, 0.228, 0.164, 0.319, 0.299, 0.249, 0.169, 0.088, 0.034 },
  /* 400 nm */ new [] { 0.065, 0.182, 0.266, 0.050, 0.328, 0.252, 0.054, 0.231, 0.144, 0.177, 0.060, 0.065, 0.136, 0.058, 0.051, 0.053, 0.283, 0.195, 0.409, 0.372, 0.289, 0.183, 0.091, 0.035 },
  /* 405 nm */ new [] { 0.068, 0.192, 0.300, 0.049, 0.385, 0.284, 0.054, 0.268, 0.145, 0.191, 0.061, 0.065, 0.156, 0.058, 0.051, 0.053, 0.322, 0.220, 0.536, 0.459, 0.324, 0.193, 0.093, 0.035 },
  /* 410 nm */ new [] { 0.068, 0.197, 0.320, 0.049, 0.418, 0.303, 0.053, 0.293, 0.144, 0.199, 0.061, 0.064, 0.175, 0.059, 0.050, 0.053, 0.343, 0.238, 0.671, 0.529, 0.346, 0.199, 0.094, 0.036 },
  /* 415 nm */ new [] { 0.067, 0.199, 0.330, 0.050, 0.437, 0.314, 0.053, 0.311, 0.141, 0.203, 0.061, 0.064, 0.193, 0.059, 0.050, 0.052, 0.354, 0.249, 0.772, 0.564, 0.354, 0.201, 0.094, 0.036 },
  /* 420 nm */ new [] { 0.064, 0.201, 0.336, 0.050, 0.446, 0.322, 0.052, 0.324, 0.138, 0.206, 0.062, 0.064, 0.208, 0.059, 0.049, 0.052, 0.359, 0.258, 0.840, 0.580, 0.357, 0.202, 0.094, 0.036 },
  /* 425 nm */ new [] { 0.062, 0.203, 0.337, 0.051, 0.448, 0.329, 0.052, 0.335, 0.134, 0.198, 0.063, 0.064, 0.224, 0.060, 0.049, 0.052, 0.357, 0.270, 0.868, 0.584, 0.358, 0.203, 0.094, 0.036 },
  /* 430 nm */ new [] { 0.059, 0.205, 0.337, 0.052, 0.448, 0.336, 0.052, 0.348, 0.132, 0.190, 0.064, 0.064, 0.244, 0.062, 0.049, 0.053, 0.350, 0.281, 0.878, 0.585, 0.359, 0.203, 0.094, 0.036 },
  /* 435 nm */ new [] { 0.057, 0.208, 0.337, 0.053, 0.447, 0.344, 0.052, 0.361, 0.132, 0.179, 0.066, 0.065, 0.265, 0.063, 0.049, 0.053, 0.339, 0.296, 0.882, 0.587, 0.360, 0.204, 0.095, 0.036 },
  /* 440 nm */ new [] { 0.055, 0.212, 0.335, 0.054, 0.444, 0.353, 0.052, 0.373, 0.131, 0.168, 0.068, 0.065, 0.290, 0.065, 0.049, 0.053, 0.327, 0.315, 0.883, 0.587, 0.361, 0.205, 0.095, 0.035 },
  /* 445 nm */ new [] { 0.054, 0.217, 0.334, 0.056, 0.440, 0.363, 0.052, 0.383, 0.131, 0.156, 0.071, 0.066, 0.316, 0.067, 0.049, 0.054, 0.313, 0.334, 0.885, 0.588, 0.362, 0.205, 0.095, 0.035 },
  /* 450 nm */ new [] { 0.053, 0.224, 0.331, 0.058, 0.434, 0.375, 0.052, 0.387, 0.129, 0.144, 0.075, 0.067, 0.335, 0.070, 0.049, 0.055, 0.298, 0.352, 0.886, 0.588, 0.362, 0.205, 0.095, 0.035 },
  /* 455 nm */ new [] { 0.053, 0.231, 0.327, 0.060, 0.428, 0.390, 0.052, 0.383, 0.128, 0.132, 0.079, 0.068, 0.342, 0.074, 0.048, 0.056, 0.282, 0.370, 0.886, 0.587, 0.361, 0.205, 0.094, 0.035 },
  /* 460 nm */ new [] { 0.052, 0.240, 0.322, 0.061, 0.421, 0.408, 0.052, 0.374, 0.126, 0.120, 0.085, 0.069, 0.338, 0.078, 0.048, 0.059, 0.267, 0.391, 0.887, 0.586, 0.361, 0.204, 0.094, 0.035 },
  /* 465 nm */ new [] { 0.052, 0.251, 0.316, 0.063, 0.413, 0.433, 0.052, 0.361, 0.126, 0.110, 0.093, 0.073, 0.324, 0.084, 0.047, 0.065, 0.253, 0.414, 0.888, 0.585, 0.359, 0.204, 0.094, 0.035 },
  /* 470 nm */ new [] { 0.052, 0.262, 0.310, 0.064, 0.405, 0.460, 0.053, 0.345, 0.125, 0.101, 0.104, 0.077, 0.302, 0.091, 0.047, 0.075, 0.239, 0.434, 0.888, 0.583, 0.358, 0.203, 0.094, 0.035 },
  /* 475 nm */ new [] { 0.053, 0.273, 0.302, 0.065, 0.394, 0.492, 0.054, 0.325, 0.123, 0.093, 0.118, 0.084, 0.273, 0.101, 0.046, 0.093, 0.225, 0.449, 0.888, 0.582, 0.358, 0.203, 0.093, 0.035 },
  /* 480 nm */ new [] { 0.054, 0.282, 0.293, 0.067, 0.381, 0.523, 0.055, 0.301, 0.119, 0.086, 0.135, 0.092, 0.239, 0.113, 0.045, 0.121, 0.209, 0.458, 0.888, 0.581, 0.357, 0.202, 0.093, 0.034 },
  /* 485 nm */ new [] { 0.055, 0.289, 0.285, 0.068, 0.372, 0.548, 0.056, 0.275, 0.114, 0.080, 0.157, 0.100, 0.205, 0.125, 0.045, 0.157, 0.195, 0.461, 0.888, 0.580, 0.356, 0.202, 0.093, 0.034 },
  /* 490 nm */ new [] { 0.057, 0.293, 0.276, 0.070, 0.362, 0.566, 0.057, 0.247, 0.109, 0.075, 0.185, 0.107, 0.172, 0.140, 0.044, 0.202, 0.182, 0.457, 0.888, 0.580, 0.356, 0.202, 0.093, 0.034 },
  /* 495 nm */ new [] { 0.059, 0.296, 0.268, 0.072, 0.352, 0.577, 0.059, 0.223, 0.105, 0.070, 0.221, 0.115, 0.144, 0.157, 0.044, 0.252, 0.172, 0.447, 0.888, 0.580, 0.356, 0.202, 0.092, 0.034 },
  /* 500 nm */ new [] { 0.061, 0.301, 0.260, 0.078, 0.342, 0.582, 0.061, 0.202, 0.103, 0.067, 0.269, 0.123, 0.120, 0.180, 0.044, 0.303, 0.163, 0.433, 0.887, 0.580, 0.356, 0.202, 0.092, 0.034 },
  /* 505 nm */ new [] { 0.062, 0.310, 0.251, 0.088, 0.330, 0.583, 0.064, 0.184, 0.102, 0.063, 0.326, 0.133, 0.101, 0.208, 0.044, 0.351, 0.155, 0.414, 0.887, 0.580, 0.356, 0.202, 0.093, 0.034 },
  /* 510 nm */ new [] { 0.065, 0.321, 0.243, 0.106, 0.314, 0.580, 0.068, 0.167, 0.100, 0.061, 0.384, 0.146, 0.086, 0.244, 0.044, 0.394, 0.146, 0.392, 0.887, 0.580, 0.356, 0.202, 0.093, 0.034 },
  /* 515 nm */ new [] { 0.067, 0.326, 0.234, 0.130, 0.294, 0.576, 0.076, 0.152, 0.097, 0.059, 0.440, 0.166, 0.074, 0.286, 0.044, 0.436, 0.135, 0.366, 0.887, 0.581, 0.356, 0.202, 0.093, 0.034 },
  /* 520 nm */ new [] { 0.070, 0.322, 0.225, 0.155, 0.271, 0.569, 0.086, 0.137, 0.094, 0.058, 0.484, 0.193, 0.066, 0.324, 0.044, 0.475, 0.124, 0.339, 0.887, 0.581, 0.357, 0.202, 0.093, 0.034 },
  /* 525 nm */ new [] { 0.072, 0.310, 0.215, 0.173, 0.249, 0.560, 0.101, 0.125, 0.091, 0.056, 0.516, 0.229, 0.059, 0.351, 0.044, 0.512, 0.113, 0.310, 0.887, 0.582, 0.357, 0.202, 0.093, 0.034 },
  /* 530 nm */ new [] { 0.074, 0.298, 0.208, 0.181, 0.231, 0.549, 0.120, 0.116, 0.089, 0.054, 0.534, 0.273, 0.054, 0.363, 0.044, 0.544, 0.106, 0.282, 0.887, 0.582, 0.357, 0.203, 0.093, 0.034 },
  /* 535 nm */ new [] { 0.075, 0.291, 0.203, 0.182, 0.219, 0.535, 0.143, 0.110, 0.090, 0.053, 0.542, 0.323, 0.051, 0.363, 0.044, 0.572, 0.102, 0.255, 0.887, 0.582, 0.358, 0.203, 0.093, 0.034 },
  /* 540 nm */ new [] { 0.076, 0.292, 0.198, 0.177, 0.211, 0.519, 0.170, 0.106, 0.092, 0.052, 0.545, 0.374, 0.048, 0.355, 0.045, 0.597, 0.102, 0.228, 0.887, 0.583, 0.358, 0.203, 0.093, 0.034 },
  /* 545 nm */ new [] { 0.078, 0.297, 0.195, 0.168, 0.209, 0.501, 0.198, 0.103, 0.096, 0.052, 0.541, 0.418, 0.046, 0.342, 0.046, 0.615, 0.105, 0.204, 0.886, 0.583, 0.358, 0.203, 0.093, 0.034 },
  /* 550 nm */ new [] { 0.079, 0.300, 0.191, 0.157, 0.209, 0.480, 0.228, 0.099, 0.102, 0.053, 0.533, 0.456, 0.045, 0.323, 0.047, 0.630, 0.107, 0.180, 0.886, 0.583, 0.358, 0.203, 0.093, 0.034 },
  /* 555 nm */ new [] { 0.082, 0.298, 0.188, 0.147, 0.207, 0.458, 0.260, 0.094, 0.106, 0.054, 0.524, 0.487, 0.044, 0.303, 0.048, 0.645, 0.107, 0.159, 0.887, 0.584, 0.358, 0.203, 0.092, 0.034 },
  /* 560 nm */ new [] { 0.087, 0.295, 0.183, 0.137, 0.201, 0.436, 0.297, 0.090, 0.108, 0.055, 0.513, 0.512, 0.043, 0.281, 0.050, 0.660, 0.106, 0.141, 0.887, 0.584, 0.359, 0.203, 0.093, 0.033 },
  /* 565 nm */ new [] { 0.092, 0.295, 0.177, 0.129, 0.196, 0.414, 0.338, 0.086, 0.109, 0.055, 0.501, 0.534, 0.042, 0.260, 0.053, 0.673, 0.107, 0.126, 0.887, 0.585, 0.359, 0.203, 0.093, 0.033 },
  /* 570 nm */ new [] { 0.100, 0.305, 0.172, 0.126, 0.196, 0.392, 0.380, 0.083, 0.112, 0.054, 0.487, 0.554, 0.041, 0.238, 0.057, 0.686, 0.112, 0.114, 0.888, 0.586, 0.360, 0.204, 0.093, 0.033 },
  /* 575 nm */ new [] { 0.107, 0.326, 0.167, 0.125, 0.199, 0.369, 0.418, 0.083, 0.126, 0.053, 0.472, 0.570, 0.041, 0.217, 0.063, 0.698, 0.123, 0.104, 0.888, 0.587, 0.361, 0.204, 0.093, 0.033 },
  /* 580 nm */ new [] { 0.115, 0.358, 0.163, 0.122, 0.206, 0.346, 0.452, 0.083, 0.157, 0.052, 0.454, 0.584, 0.040, 0.196, 0.072, 0.708, 0.141, 0.097, 0.887, 0.588, 0.361, 0.205, 0.093, 0.033 },
  /* 585 nm */ new [] { 0.122, 0.397, 0.160, 0.119, 0.215, 0.324, 0.481, 0.085, 0.208, 0.052, 0.436, 0.598, 0.040, 0.177, 0.086, 0.718, 0.166, 0.092, 0.886, 0.588, 0.361, 0.205, 0.093, 0.033 },
  /* 590 nm */ new [] { 0.129, 0.435, 0.157, 0.115, 0.223, 0.302, 0.503, 0.086, 0.274, 0.053, 0.416, 0.609, 0.040, 0.158, 0.109, 0.726, 0.198, 0.088, 0.886, 0.588, 0.361, 0.205, 0.093, 0.033 },
  /* 595 nm */ new [] { 0.134, 0.468, 0.153, 0.109, 0.229, 0.279, 0.520, 0.087, 0.346, 0.055, 0.394, 0.617, 0.040, 0.140, 0.143, 0.732, 0.235, 0.083, 0.886, 0.588, 0.361, 0.205, 0.092, 0.033 },
  /* 600 nm */ new [] { 0.138, 0.494, 0.150, 0.104, 0.235, 0.260, 0.532, 0.087, 0.415, 0.059, 0.374, 0.624, 0.039, 0.124, 0.192, 0.737, 0.279, 0.080, 0.887, 0.588, 0.360, 0.204, 0.092, 0.033 },
  /* 605 nm */ new [] { 0.142, 0.514, 0.147, 0.100, 0.241, 0.245, 0.543, 0.086, 0.473, 0.065, 0.358, 0.630, 0.039, 0.111, 0.256, 0.742, 0.333, 0.077, 0.888, 0.587, 0.360, 0.204, 0.092, 0.033 },
  /* 610 nm */ new [] { 0.146, 0.530, 0.144, 0.098, 0.245, 0.234, 0.552, 0.085, 0.517, 0.074, 0.346, 0.635, 0.040, 0.101, 0.332, 0.746, 0.394, 0.075, 0.889, 0.586, 0.359, 0.204, 0.092, 0.033 },
  /* 615 nm */ new [] { 0.150, 0.541, 0.141, 0.097, 0.245, 0.226, 0.560, 0.084, 0.547, 0.086, 0.337, 0.640, 0.040, 0.094, 0.413, 0.749, 0.460, 0.074, 0.890, 0.586, 0.358, 0.203, 0.091, 0.033 },
  /* 620 nm */ new [] { 0.154, 0.550, 0.137, 0.098, 0.243, 0.221, 0.566, 0.084, 0.567, 0.099, 0.331, 0.645, 0.040, 0.089, 0.486, 0.753, 0.522, 0.073, 0.891, 0.585, 0.357, 0.203, 0.091, 0.033 },
  /* 625 nm */ new [] { 0.158, 0.557, 0.133, 0.100, 0.243, 0.217, 0.572, 0.085, 0.582, 0.113, 0.328, 0.650, 0.040, 0.086, 0.550, 0.757, 0.580, 0.073, 0.891, 0.584, 0.356, 0.202, 0.091, 0.033 },
  /* 630 nm */ new [] { 0.163, 0.564, 0.130, 0.100, 0.247, 0.215, 0.578, 0.088, 0.591, 0.126, 0.325, 0.654, 0.041, 0.084, 0.598, 0.761, 0.628, 0.073, 0.891, 0.583, 0.355, 0.201, 0.090, 0.033 },
  /* 635 nm */ new [] { 0.167, 0.569, 0.126, 0.099, 0.254, 0.212, 0.583, 0.092, 0.597, 0.138, 0.322, 0.658, 0.041, 0.082, 0.631, 0.765, 0.666, 0.073, 0.891, 0.581, 0.354, 0.201, 0.090, 0.033 },
  /* 640 nm */ new [] { 0.173, 0.574, 0.123, 0.097, 0.269, 0.210, 0.587, 0.098, 0.601, 0.149, 0.320, 0.662, 0.042, 0.080, 0.654, 0.768, 0.696, 0.073, 0.890, 0.580, 0.353, 0.200, 0.090, 0.033 },
  /* 645 nm */ new [] { 0.180, 0.582, 0.120, 0.096, 0.291, 0.209, 0.593, 0.105, 0.604, 0.161, 0.319, 0.667, 0.042, 0.078, 0.672, 0.772, 0.722, 0.073, 0.889, 0.579, 0.352, 0.199, 0.090, 0.033 },
  /* 650 nm */ new [] { 0.188, 0.590, 0.118, 0.095, 0.318, 0.208, 0.599, 0.111, 0.607, 0.172, 0.319, 0.672, 0.042, 0.077, 0.686, 0.777, 0.742, 0.074, 0.889, 0.578, 0.351, 0.198, 0.089, 0.033 },
  /* 655 nm */ new [] { 0.196, 0.597, 0.115, 0.095, 0.351, 0.209, 0.602, 0.118, 0.608, 0.182, 0.320, 0.675, 0.043, 0.076, 0.694, 0.779, 0.756, 0.075, 0.889, 0.577, 0.350, 0.198, 0.089, 0.033 },
  /* 660 nm */ new [] { 0.204, 0.605, 0.112, 0.095, 0.384, 0.211, 0.604, 0.123, 0.607, 0.193, 0.324, 0.676, 0.043, 0.075, 0.700, 0.780, 0.766, 0.076, 0.889, 0.576, 0.349, 0.197, 0.089, 0.033 },
  /* 665 nm */ new [] { 0.213, 0.614, 0.110, 0.097, 0.417, 0.215, 0.606, 0.126, 0.606, 0.205, 0.330, 0.677, 0.043, 0.075, 0.704, 0.780, 0.774, 0.076, 0.889, 0.575, 0.348, 0.197, 0.088, 0.033 },
  /* 670 nm */ new [] { 0.222, 0.624, 0.108, 0.101, 0.446, 0.220, 0.608, 0.126, 0.605, 0.217, 0.337, 0.678, 0.044, 0.075, 0.707, 0.781, 0.780, 0.077, 0.888, 0.574, 0.346, 0.196, 0.088, 0.033 },
  /* 675 nm */ new [] { 0.231, 0.637, 0.106, 0.110, 0.470, 0.227, 0.611, 0.124, 0.605, 0.232, 0.345, 0.681, 0.044, 0.077, 0.712, 0.782, 0.785, 0.076, 0.888, 0.573, 0.346, 0.195, 0.088, 0.033 },
  /* 680 nm */ new [] { 0.242, 0.652, 0.105, 0.125, 0.490, 0.233, 0.615, 0.120, 0.605, 0.248, 0.354, 0.685, 0.044, 0.078, 0.718, 0.785, 0.791, 0.075, 0.888, 0.572, 0.345, 0.195, 0.087, 0.033 },
  /* 685 nm */ new [] { 0.251, 0.668, 0.104, 0.147, 0.504, 0.239, 0.619, 0.117, 0.604, 0.266, 0.362, 0.688, 0.044, 0.080, 0.721, 0.785, 0.794, 0.074, 0.888, 0.571, 0.344, 0.194, 0.087, 0.033 },
  /* 690 nm */ new [] { 0.261, 0.682, 0.104, 0.174, 0.511, 0.244, 0.622, 0.115, 0.605, 0.282, 0.368, 0.690, 0.045, 0.082, 0.724, 0.787, 0.798, 0.074, 0.888, 0.570, 0.343, 0.194, 0.087, 0.032 },
  /* 695 nm */ new [] { 0.271, 0.697, 0.103, 0.210, 0.517, 0.249, 0.625, 0.115, 0.606, 0.301, 0.375, 0.693, 0.046, 0.085, 0.727, 0.789, 0.801, 0.073, 0.888, 0.569, 0.342, 0.193, 0.087, 0.032 },
  /* 700 nm */ new [] { 0.282, 0.713, 0.103, 0.247, 0.520, 0.252, 0.628, 0.116, 0.606, 0.319, 0.379, 0.696, 0.048, 0.088, 0.729, 0.792, 0.804, 0.072, 0.888, 0.568, 0.341, 0.192, 0.086, 0.032 },
  /* 705 nm */ new [] { 0.294, 0.728, 0.102, 0.283, 0.522, 0.252, 0.630, 0.118, 0.604, 0.338, 0.381, 0.698, 0.050, 0.089, 0.730, 0.792, 0.806, 0.072, 0.887, 0.567, 0.340, 0.192, 0.086, 0.032 },
  /* 710 nm */ new [] { 0.305, 0.745, 0.102, 0.311, 0.523, 0.250, 0.633, 0.120, 0.602, 0.355, 0.379, 0.698, 0.051, 0.089, 0.730, 0.793, 0.807, 0.071, 0.886, 0.566, 0.339, 0.191, 0.086, 0.032 },
  /* 715 nm */ new [] { 0.318, 0.753, 0.102, 0.329, 0.522, 0.248, 0.633, 0.124, 0.601, 0.371, 0.376, 0.698, 0.053, 0.090, 0.729, 0.792, 0.807, 0.073, 0.886, 0.565, 0.338, 0.191, 0.086, 0.032 },
  /* 720 nm */ new [] { 0.334, 0.762, 0.102, 0.343, 0.521, 0.244, 0.633, 0.128, 0.599, 0.388, 0.373, 0.698, 0.056, 0.090, 0.727, 0.790, 0.807, 0.075, 0.886, 0.564, 0.337, 0.190, 0.085, 0.032 },
  /* 725 nm */ new [] { 0.354, 0.774, 0.102, 0.353, 0.521, 0.245, 0.636, 0.133, 0.598, 0.406, 0.372, 0.700, 0.060, 0.090, 0.728, 0.792, 0.810, 0.078, 0.885, 0.562, 0.336, 0.189, 0.085, 0.032 },
  /* 730 nm */ new [] { 0.372, 0.783, 0.102, 0.358, 0.522, 0.245, 0.637, 0.139, 0.596, 0.422, 0.375, 0.701, 0.064, 0.089, 0.729, 0.792, 0.813, 0.082, 0.885, 0.562, 0.335, 0.189, 0.085, 0.032 },
  /* 735 nm */ new [] { 0.392, 0.788, 0.104, 0.362, 0.521, 0.251, 0.639, 0.149, 0.595, 0.436, 0.382, 0.701, 0.070, 0.092, 0.729, 0.790, 0.814, 0.090, 0.885, 0.560, 0.334, 0.188, 0.085, 0.032 },
  /* 740 nm */ new [] { 0.409, 0.791, 0.104, 0.364, 0.521, 0.260, 0.638, 0.162, 0.593, 0.451, 0.392, 0.701, 0.079, 0.094, 0.727, 0.787, 0.813, 0.100, 0.884, 0.560, 0.333, 0.188, 0.085, 0.032 },
  /* 745 nm */ new [] { 0.420, 0.787, 0.104, 0.360, 0.516, 0.269, 0.633, 0.178, 0.587, 0.460, 0.401, 0.695, 0.091, 0.097, 0.723, 0.782, 0.810, 0.116, 0.884, 0.558, 0.332, 0.187, 0.084, 0.032 },
  /* 750 nm */ new [] { 0.436, 0.789, 0.104, 0.362, 0.514, 0.278, 0.633, 0.197, 0.584, 0.471, 0.412, 0.694, 0.104, 0.102, 0.721, 0.778, 0.808, 0.133, 0.883, 0.557, 0.331, 0.187, 0.084, 0.032 },
  /* 755 nm */ new [] { 0.450, 0.794, 0.106, 0.364, 0.514, 0.288, 0.636, 0.219, 0.584, 0.481, 0.422, 0.696, 0.120, 0.106, 0.724, 0.780, 0.811, 0.154, 0.882, 0.556, 0.330, 0.186, 0.084, 0.032 },
  /* 760 nm */ new [] { 0.462, 0.801, 0.106, 0.368, 0.517, 0.297, 0.641, 0.242, 0.586, 0.492, 0.433, 0.700, 0.138, 0.110, 0.728, 0.782, 0.814, 0.176, 0.882, 0.555, 0.329, 0.185, 0.084, 0.032 },
  /* 765 nm */ new [] { 0.465, 0.799, 0.107, 0.368, 0.515, 0.301, 0.639, 0.259, 0.584, 0.495, 0.436, 0.698, 0.154, 0.111, 0.727, 0.781, 0.813, 0.191, 0.881, 0.554, 0.328, 0.185, 0.084, 0.032 },
  /* 770 nm */ new [] { 0.448, 0.771, 0.110, 0.355, 0.500, 0.297, 0.616, 0.275, 0.566, 0.482, 0.426, 0.673, 0.168, 0.112, 0.702, 0.752, 0.785, 0.200, 0.880, 0.553, 0.327, 0.184, 0.083, 0.032 },
  /* 775 nm */ new [] { 0.432, 0.747, 0.115, 0.346, 0.491, 0.296, 0.598, 0.294, 0.551, 0.471, 0.413, 0.653, 0.186, 0.112, 0.680, 0.728, 0.765, 0.208, 0.880, 0.551, 0.326, 0.184, 0.083, 0.032 },
  /* 780 nm */ new [] { 0.421, 0.734, 0.120, 0.341, 0.487, 0.296, 0.582, 0.316, 0.540, 0.467, 0.404, 0.639, 0.204, 0.112, 0.664, 0.710, 0.752, 0.214, 0.879, 0.550, 0.325, 0.183, 0.083, 0.032 }
};

        public static double[] macbeth_lambda = new[]
            {
                 380.0,
                 385.0,
                 390.0,
                 395.0,
                 400.0,
                 405.0,
                 410.0,
                 415.0,
                 420.0,
                 425.0,
                 430.0,
                 435.0,
                 440.0,
                 445.0,
                 450.0,
                 455.0,
                 460.0,
                 465.0,
                 470.0,
                 475.0,
                 480.0,
                 485.0,
                 490.0,
                 495.0,
                 500.0,
                 505.0,
                 510.0,
                 515.0,
                 520.0,
                 525.0,
                 530.0,
                 535.0,
                 540.0,
                 545.0,
                 550.0,
                 555.0,
                 560.0,
                 565.0,
                 570.0,
                 575.0,
                 580.0,
                 585.0,
                 590.0,
                 595.0,
                 600.0,
                 605.0,
                 610.0,
                 615.0,
                 620.0,
                 625.0,
                 630.0,
                 635.0,
                 640.0,
                 645.0,
                 650.0,
                 655.0,
                 660.0,
                 665.0,
                 670.0,
                 675.0,
                 680.0,
                 685.0,
                 690.0,
                 695.0,
                 700.0,
                 705.0,
                 710.0,
                 715.0,
                 720.0,
                 725.0,
                 730.0,
                 735.0,
                 740.0,
                 745.0,
                 750.0,
                 755.0,
                 760.0,
                 765.0,
                 770.0,
                 775.0,
                 780.0,
            };

        #endregion

        #region ISpectrum Members

        public float this[int index]
        {
            get
            {
                fixed (float* ptr = spectra)
                {
                    return ptr[index];
                }
            }
            set
            {
                fixed (float* ptr = spectra)
                {
                    ptr[index] = value;
                }
            }
        }

        #endregion


        public static void MemCopy(float[] data, float* arr)
        {
            Marshal.Copy(data, 0, new IntPtr(arr), data.Length);
        }

        public void TransformToRgb(out RgbSpectrum result)
        {
            result = this.ToRgb();
        }

        public byte[] Serialize()
        {
            var data = new List<byte>(nSamples * 4);
            for (int i = 0; i < nSamples; i++)
            {
                data.AddRange(BitConverter.GetBytes(this[i]));
            }
            return data.ToArray();

        }

        public void Deserialize(byte[] data, int offset = 0)
        {
            for (int i = 0; i < nSamples; i++)
            {
                var c = BitConverter.ToSingle(data, i * 4 + offset);
                this[i] = c;
            }
        }

        public string SerializeToString()
        {
            return this.ToString();
        }

        public void FromString(string data)
        {
            throw new NotImplementedException();
        }

        [DllImport("kernel32.dll", EntryPoint = "RtlCopyMemory")]
        static extern void CopyMemory(IntPtr Destination, IntPtr Source, uint Length);


        public void Mul(float s)
        {
            this *= s;
        }

        public RgbSpectrum ToXyz()
        {
            var xyz = new float[3];
            this.XYZ(ref xyz);
            return new RgbSpectrum(xyz);
        }
    }
}